From 52a243afcabde3ccc375aaf6e79a9f50c046f54b Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 25 Aug 2021 21:42:15 +0300 Subject: [PATCH 001/321] Don't select random beatmap when previous was filtered --- .../SongSelect/TestSceneBeatmapCarousel.cs | 99 +++++++++++-------- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Carousel/CarouselGroupEagerSelect.cs | 25 ++++- 3 files changed, 82 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 78ddfa9ed2..f42ff3811a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -616,55 +616,72 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1); } - [Test] - public void TestRandomFallbackOnNonMatchingPrevious() + [TestCase(10, 1)] + [TestCase(10, 10)] + public void TestCarouselSelectsNextWhenPreviousIsFiltered(int makeThisManyGroups, int haveThisManySetsInGroup) { - List manySets = new List(); + List sets = new List(); - AddStep("populate maps", () => + for (int i = 0; i < makeThisManyGroups; i++) { - for (int i = 0; i < 10; i++) + for (int j = 0; j < haveThisManySetsInGroup; j++) { - var set = createTestBeatmapSet(i); - - foreach (var b in set.Beatmaps) + var testBeatmap = createTestBeatmapSet(i * haveThisManySetsInGroup + j + 1); + var rulesetID = i % 3; + testBeatmap.Beatmaps.ForEach(b => { - // all taiko except for first - int ruleset = i > 0 ? 1 : 0; - - b.Ruleset = rulesets.GetRuleset(ruleset); - b.RulesetID = ruleset; - } - - manySets.Add(set); + b.Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID); + b.RulesetID = rulesetID; + }); + sets.Add(testBeatmap); } - }); - - loadBeatmaps(manySets); - - for (int i = 0; i < 10; i++) - { - AddStep("Reset filter", () => carousel.Filter(new FilterCriteria(), false)); - - AddStep("select first beatmap", () => carousel.SelectBeatmap(manySets.First().Beatmaps.First())); - - AddStep("Toggle non-matching filter", () => - { - carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false); - }); - - AddAssert("selection lost", () => carousel.SelectedBeatmap == null); - - AddStep("Restore different ruleset filter", () => - { - carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false); - eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID); - }); - - AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First()); } - AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2); + loadBeatmaps(sets); + setSelected(1, 1); + advanceSelection(false); + + for (int i = 1; i < makeThisManyGroups; i++) + { + var rulesetID = i % 3; + AddStep($"Toggle filter to ruleset {rulesetID}", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID) }, false); + carousel.Filter(new FilterCriteria(), false); + }); + waitForSelection(i * haveThisManySetsInGroup + 1); + } + } + + [Test] + public void TestCarouselSelectsBackwardsWhenPreviousIsFilteredNearTheEnd() + { + List sets = new List(); + + for (int i = 1; i <= 40; i++) + { + var testBeatmap = createTestBeatmapSet(i); + var rulesetID = (i - 1) / 10; + testBeatmap.Beatmaps.ForEach(b => + { + b.Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID); + b.RulesetID = rulesetID; + }); + sets.Add(testBeatmap); + } + + loadBeatmaps(sets); + + for (int i = 1; i < 4; i++) + { + setSelected(i * 10 + 1, 1); + AddStep("Toggle filter to ruleset 0", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false); + carousel.Filter(new FilterCriteria(), false); + }); + waitForSelection(10); + } } [Test] diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b05b7aeb32..7c1403da82 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -911,7 +911,7 @@ namespace osu.Game.Screens.Select protected override void PerformSelection() { - if (LastSelected == null || LastSelected.Filtered.Value) + if (LastSelected == null) carousel?.SelectNextRandom(); else base.PerformSelection(); diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 9e8aad4b6f..79bcdbf1c1 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -101,8 +101,29 @@ namespace osu.Game.Screens.Select.Carousel protected virtual CarouselItem GetNextToSelect() { - return Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value) ?? - Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value); + // our return value should be the item (that is not filtered and) that is nearest to the previously selected one + + // find nearest such item going forwards in selection + int forwardsDistance = 0; + var forwards = Children.Skip(lastSelectedIndex).SkipWhile((c) => + { + forwardsDistance++; + return c.Filtered.Value; + }).FirstOrDefault(); + // and backwards + int backwardsDistance = 0; + var backwards = Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex - 1).SkipWhile((c) => + { + backwardsDistance++; + return c.Filtered.Value; + }).FirstOrDefault(); + + // if only one direction had such an item, return that + if (forwards == null || backwards == null) + return forwards ?? backwards; + + // else return the closest item + return forwardsDistance < backwardsDistance ? forwards : backwards; } protected virtual void PerformSelection() From b484bd1af3ab2e49387d4eb9bb6f09b86b035cdf Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 25 Aug 2021 22:43:58 +0300 Subject: [PATCH 002/321] Rename parameter and remove redundant parentheses --- .../Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 79bcdbf1c1..2894209206 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -105,17 +105,17 @@ namespace osu.Game.Screens.Select.Carousel // find nearest such item going forwards in selection int forwardsDistance = 0; - var forwards = Children.Skip(lastSelectedIndex).SkipWhile((c) => + var forwards = Children.Skip(lastSelectedIndex).SkipWhile(item => { forwardsDistance++; - return c.Filtered.Value; + return item.Filtered.Value; }).FirstOrDefault(); // and backwards int backwardsDistance = 0; - var backwards = Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex - 1).SkipWhile((c) => + var backwards = Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex - 1).SkipWhile(item => { backwardsDistance++; - return c.Filtered.Value; + return item.Filtered.Value; }).FirstOrDefault(); // if only one direction had such an item, return that From a8d869c31cace6e8b47bda6f972459b07af50119 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 26 Aug 2021 18:31:19 +0300 Subject: [PATCH 003/321] Replace `SkipWhile()` LINQ with a while loop --- .../Carousel/CarouselGroupEagerSelect.cs | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 2894209206..aedeac04d3 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -99,31 +99,32 @@ namespace osu.Game.Screens.Select.Carousel PerformSelection(); } + /// + /// Finds the item this group would select next if it attempted selection + /// + /// An unfiltered item nearest to the last selected one or null if all items are filtered protected virtual CarouselItem GetNextToSelect() { - // our return value should be the item (that is not filtered and) that is nearest to the previously selected one + int forwardsIndex = lastSelectedIndex; + int backwardsIndex = lastSelectedIndex; - // find nearest such item going forwards in selection - int forwardsDistance = 0; - var forwards = Children.Skip(lastSelectedIndex).SkipWhile(item => + while (true) { - forwardsDistance++; - return item.Filtered.Value; - }).FirstOrDefault(); - // and backwards - int backwardsDistance = 0; - var backwards = Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex - 1).SkipWhile(item => - { - backwardsDistance++; - return item.Filtered.Value; - }).FirstOrDefault(); + if (forwardsIndex >= Children.Count) + return Children.Reverse().Skip(Children.Count - backwardsIndex).FirstOrDefault(item => !item.Filtered.Value); - // if only one direction had such an item, return that - if (forwards == null || backwards == null) - return forwards ?? backwards; + if (backwardsIndex < 0) + return Children.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); - // else return the closest item - return forwardsDistance < backwardsDistance ? forwards : backwards; + if (!Children[forwardsIndex].Filtered.Value) + return Children[forwardsIndex]; + + if (!Children[backwardsIndex].Filtered.Value) + return Children[backwardsIndex]; + + forwardsIndex++; + backwardsIndex--; + } } protected virtual void PerformSelection() From 8bd7837bf747600e67ffdc8f06062b847e10cd20 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Thu, 26 Aug 2021 19:29:58 +0300 Subject: [PATCH 004/321] Fix off-by-one skip amount --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index aedeac04d3..9793b69eca 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Select.Carousel while (true) { if (forwardsIndex >= Children.Count) - return Children.Reverse().Skip(Children.Count - backwardsIndex).FirstOrDefault(item => !item.Filtered.Value); + return Children.Reverse().Skip(Children.Count - backwardsIndex - 1).FirstOrDefault(item => !item.Filtered.Value); if (backwardsIndex < 0) return Children.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); From 43e8e3e9229cef1d5ac72203d8e71c0f7584c49f Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Fri, 27 Aug 2021 18:16:53 +0300 Subject: [PATCH 005/321] Add comments to `GetNextToSelect()` --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 9793b69eca..cdd6d48e66 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -110,18 +110,19 @@ namespace osu.Game.Screens.Select.Carousel while (true) { + // check if a direction has been exhausted and an item (or null) from the other direction should be returned if (forwardsIndex >= Children.Count) return Children.Reverse().Skip(Children.Count - backwardsIndex - 1).FirstOrDefault(item => !item.Filtered.Value); - if (backwardsIndex < 0) return Children.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); + // check if an unfiltered item has been reached if (!Children[forwardsIndex].Filtered.Value) return Children[forwardsIndex]; - if (!Children[backwardsIndex].Filtered.Value) return Children[backwardsIndex]; + // increment the indices forwardsIndex++; backwardsIndex--; } From 8e84f76bf9547d349e25090423468c9e2efb71fd Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Fri, 8 Jul 2022 15:49:10 -0400 Subject: [PATCH 006/321] add hidden item toggle to directory/file selectors --- .../UserInterfaceV2/OsuDirectorySelector.cs | 2 ++ .../OsuDirectorySelectorHiddenToggle.cs | 29 +++++++++++++++++++ .../UserInterfaceV2/OsuFileSelector.cs | 2 ++ 3 files changed, 33 insertions(+) create mode 100644 osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs index 42e1073baf..0e348108aa 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs @@ -31,6 +31,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay(); + protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } }; + protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory); protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName); diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs new file mode 100644 index 0000000000..bb582cad8f --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osuTK.Graphics; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox + { + public OsuDirectorySelectorHiddenToggle() + { + RelativeSizeAxes = Axes.None; + AutoSizeAxes = Axes.Both; + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Nub.AccentColour = colours.GreySeaFoamLighter; + Nub.GlowingAccentColour = Color4.White; + Nub.GlowColour = Color4.White; + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs index 3e8b7dc209..70af68d595 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs @@ -33,6 +33,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay(); + protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } }; + protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory); protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName); From b92979acd69b61330d1456a15dafe682df5756bd Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Fri, 8 Jul 2022 16:00:48 -0400 Subject: [PATCH 007/321] add tooltip to checkbox --- .../UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs index bb582cad8f..5b5af1d71d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -3,13 +3,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { - internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox + internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox, IHasTooltip { + public LocalisableString TooltipText => @"Show hidden items"; + public OsuDirectorySelectorHiddenToggle() { RelativeSizeAxes = Axes.None; From 84002aefae779efefc9602215716e43542a9d0ac Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jul 2022 20:18:50 +0300 Subject: [PATCH 008/321] Update file/directory selector tests to use `ThemeComparisonTestScene` --- .../Settings/TestSceneDirectorySelector.cs | 11 +++--- .../Visual/Settings/TestSceneFileSelector.cs | 34 +++++++++++++++---- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs index 4f05194e08..16110e5595 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs @@ -3,18 +3,17 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Tests.Visual.UserInterface; namespace osu.Game.Tests.Visual.Settings { - public class TestSceneDirectorySelector : OsuTestScene + public class TestSceneDirectorySelector : ThemeComparisonTestScene { - [BackgroundDependencyLoader] - private void load() + protected override Drawable CreateContent() => new OsuDirectorySelector { - Add(new OsuDirectorySelector { RelativeSizeAxes = Axes.Both }); - } + RelativeSizeAxes = Axes.Both + }; } } diff --git a/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs index 6f25012bfa..97bf0d212a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs @@ -4,23 +4,43 @@ #nullable disable using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Tests.Visual.UserInterface; namespace osu.Game.Tests.Visual.Settings { - public class TestSceneFileSelector : OsuTestScene + public class TestSceneFileSelector : ThemeComparisonTestScene { - [Test] - public void TestAllFiles() - { - AddStep("create", () => Child = new OsuFileSelector { RelativeSizeAxes = Axes.Both }); - } + [Resolved] + private OsuColour colours { get; set; } [Test] public void TestJpgFilesOnly() { - AddStep("create", () => Child = new OsuFileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both }); + AddStep("create", () => + { + Cell(0, 0).Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.GreySeaFoam + }, + new OsuFileSelector(validFileExtensions: new[] { ".jpg" }) + { + RelativeSizeAxes = Axes.Both, + }, + }; + }); } + + protected override Drawable CreateContent() => new OsuFileSelector + { + RelativeSizeAxes = Axes.Both, + }; } } From 7d26f178c6c1b8b3bc7b56e20352c52389c3bd75 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 11 Jul 2022 16:36:17 -0400 Subject: [PATCH 009/321] use `OverlayColourProvider` for nub colors when possible --- .../UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs index 5b5af1d71d..74e9312eda 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 @@ -22,9 +23,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 Origin = Anchor.CentreLeft; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(true)] + private void load(OverlayColourProvider? overlayColourProvider, OsuColour colours) { + if (overlayColourProvider != null) + return; + Nub.AccentColour = colours.GreySeaFoamLighter; Nub.GlowingAccentColour = Color4.White; Nub.GlowColour = Color4.White; From 77f5ec3a4ec24dcc6b5df8b7b7fd2dd1a8657dd4 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 11 Jul 2022 16:39:53 -0400 Subject: [PATCH 010/321] use checkbox label instead of tooltip --- .../OsuDirectorySelectorHiddenToggle.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs index 74e9312eda..38ec602fe7 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -3,24 +3,23 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osuTK; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { - internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox, IHasTooltip + internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox { - public LocalisableString TooltipText => @"Show hidden items"; - public OsuDirectorySelectorHiddenToggle() { RelativeSizeAxes = Axes.None; - AutoSizeAxes = Axes.Both; + AutoSizeAxes = Axes.None; + Size = new Vector2(100, 50); Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; + LabelText = "Show hidden items"; } [BackgroundDependencyLoader(true)] From 8617b94c9d6865a65007acbc79010f89b2c04be8 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 11 Jul 2022 17:12:14 -0400 Subject: [PATCH 011/321] shorten label --- .../UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs index 38ec602fe7..28f6049683 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -19,7 +19,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Size = new Vector2(100, 50); Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; - LabelText = "Show hidden items"; + LabelText = @"Show hidden"; } [BackgroundDependencyLoader(true)] From d6abdc597d68459fd6539d49123a5e5cec7f7051 Mon Sep 17 00:00:00 2001 From: Gabe Livengood <47010459+ggliv@users.noreply.github.com> Date: Mon, 11 Jul 2022 17:12:41 -0400 Subject: [PATCH 012/321] correct label positioning --- .../Graphics/UserInterface/OsuCheckbox.cs | 20 +++++++++---------- .../OsuDirectorySelectorHiddenToggle.cs | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index bbd8f8ecea..8772c1e2d9 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -26,24 +26,24 @@ namespace osu.Game.Graphics.UserInterface { set { - if (labelText != null) - labelText.Text = value; + if (LabelTextFlowContainer != null) + LabelTextFlowContainer.Text = value; } } public MarginPadding LabelPadding { - get => labelText?.Padding ?? new MarginPadding(); + get => LabelTextFlowContainer?.Padding ?? new MarginPadding(); set { - if (labelText != null) - labelText.Padding = value; + if (LabelTextFlowContainer != null) + LabelTextFlowContainer.Padding = value; } } protected readonly Nub Nub; - private readonly OsuTextFlowContainer labelText; + protected readonly OsuTextFlowContainer LabelTextFlowContainer; private Sample sampleChecked; private Sample sampleUnchecked; @@ -56,7 +56,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - labelText = new OsuTextFlowContainer(ApplyLabelParameters) + LabelTextFlowContainer = new OsuTextFlowContainer(ApplyLabelParameters) { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -70,19 +70,19 @@ namespace osu.Game.Graphics.UserInterface Nub.Anchor = Anchor.CentreRight; Nub.Origin = Anchor.CentreRight; Nub.Margin = new MarginPadding { Right = nub_padding }; - labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 }; } else { Nub.Anchor = Anchor.CentreLeft; Nub.Origin = Anchor.CentreLeft; Nub.Margin = new MarginPadding { Left = nub_padding }; - labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 }; } Nub.Current.BindTo(Current); - Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; + Current.DisabledChanged += disabled => LabelTextFlowContainer.Alpha = Nub.Alpha = disabled ? 0.3f : 1; } /// diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs index 28f6049683..7aaf12ca34 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorHiddenToggle.cs @@ -19,6 +19,8 @@ namespace osu.Game.Graphics.UserInterfaceV2 Size = new Vector2(100, 50); Anchor = Anchor.CentreLeft; Origin = Anchor.CentreLeft; + LabelTextFlowContainer.Anchor = Anchor.CentreLeft; + LabelTextFlowContainer.Origin = Anchor.CentreLeft; LabelText = @"Show hidden"; } From 7add3a69508abd2d2cffc9f9ec0ebe329b9faa26 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Thu, 18 Aug 2022 19:00:54 +0200 Subject: [PATCH 013/321] ar var test --- osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs new file mode 100644 index 0000000000..e291e92901 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModFlash : ModWithVisibilityAdjustment, IApplicableAfterBeatmapConversion + { + public override string Name => "Var AR Test"; + public override string Acronym => "TP"; + public override ModType Type => ModType.Fun; + public override IconUsage? Icon => FontAwesome.Regular.Sun; + public override string Description => @"how far will your reading stretch"; + public override double ScoreMultiplier => 1.03; + + public override void ApplyToBeatmap(IBeatmap beatmap) + { + base.ApplyToBeatmap(beatmap); + double lastObjectEnd = beatmap.HitObjects.LastOrDefault()?.GetEndTime() ?? 0; + + foreach (var obj in beatmap.HitObjects.OfType()) + applyVariableAr(obj); + + void applyVariableAr(OsuHitObject osuObject) + { + double percentageofmap = osuObject.StartTime / lastObjectEnd; + osuObject.TimePreempt = (percentageofmap * osuObject.TimePreempt) * ARadded.Value + osuObject.TimePreempt; + foreach (var nested in osuObject.NestedHitObjects.OfType()) + applyVariableAr(nested); + } + } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + [SettingSource("Additional AR", "how much AR change to add")] + public BindableDouble ARadded { get; } = new BindableDouble(1) + { + Precision = 0.1f, + MinValue = 0, + MaxValue = 10, + }; + + /*[SettingSource("Additional Ar", "how much ar change to add")] + public BindableBool scaledown { get; } = new BindableBool();*/ + } +} From 217fa00a72af23ae2971dba4d10beb9a5a185930 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Aug 2022 19:32:49 +0900 Subject: [PATCH 014/321] Bring logic up-to-date --- .../SongSelect/TestSceneBeatmapCarousel.cs | 22 +++---------------- .../Carousel/CarouselGroupEagerSelect.cs | 14 ++++++------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 8f7d630a19..7623704a4b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -834,16 +834,7 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 0; i < makeThisManyGroups; i++) { for (int j = 0; j < haveThisManySetsInGroup; j++) - { - var testBeatmap = createTestBeatmapSet(i * haveThisManySetsInGroup + j + 1); - var rulesetID = i % 3; - testBeatmap.Beatmaps.ForEach(b => - { - b.Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID); - b.RulesetID = rulesetID; - }); - sets.Add(testBeatmap); - } + sets.Add(TestResources.CreateTestBeatmapSetInfo(i * haveThisManySetsInGroup + j + 1)); } loadBeatmaps(sets); @@ -852,7 +843,7 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 1; i < makeThisManyGroups; i++) { - var rulesetID = i % 3; + int rulesetID = i % 3; AddStep($"Toggle filter to ruleset {rulesetID}", () => { carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID) }, false); @@ -869,14 +860,7 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 1; i <= 40; i++) { - var testBeatmap = createTestBeatmapSet(i); - var rulesetID = (i - 1) / 10; - testBeatmap.Beatmaps.ForEach(b => - { - b.Ruleset = rulesets.AvailableRulesets.ElementAt(rulesetID); - b.RulesetID = rulesetID; - }); - sets.Add(testBeatmap); + sets.Add(TestResources.CreateTestBeatmapSetInfo(i)); } loadBeatmaps(sets); diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 432f795f4c..2c1ccebfab 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -120,16 +120,16 @@ namespace osu.Game.Screens.Select.Carousel while (true) { // check if a direction has been exhausted and an item (or null) from the other direction should be returned - if (forwardsIndex >= Children.Count) - return Children.Reverse().Skip(Children.Count - backwardsIndex - 1).FirstOrDefault(item => !item.Filtered.Value); + if (forwardsIndex >= Items.Count) + return Items.Reverse().Skip(Items.Count - backwardsIndex - 1).FirstOrDefault(item => !item.Filtered.Value); if (backwardsIndex < 0) - return Children.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); + return Items.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); // check if an unfiltered item has been reached - if (!Children[forwardsIndex].Filtered.Value) - return Children[forwardsIndex]; - if (!Children[backwardsIndex].Filtered.Value) - return Children[backwardsIndex]; + if (!Items[forwardsIndex].Filtered.Value) + return Items[forwardsIndex]; + if (!Items[backwardsIndex].Filtered.Value) + return Items[backwardsIndex]; // increment the indices forwardsIndex++; From 5a1b2f9a77b62f0ef01e010bfef942ff68385b9f Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Thu, 8 Sep 2022 00:05:48 +0200 Subject: [PATCH 015/321] Freeze frame testing --- osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs | 85 ++++++++++++++--------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs index e291e92901..b50de12465 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs @@ -1,61 +1,84 @@ -// 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.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFlash : ModWithVisibilityAdjustment, IApplicableAfterBeatmapConversion + public class OsuModFlash : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset { - public override string Name => "Var AR Test"; - public override string Acronym => "TP"; + public override string Name => "Freeze frame"; + + public override string Acronym => "FF"; + + public override double ScoreMultiplier => 1; + + public override string Description => "Burn the notes into your memory"; + public override ModType Type => ModType.Fun; - public override IconUsage? Icon => FontAwesome.Regular.Sun; - public override string Description => @"how far will your reading stretch"; - public override double ScoreMultiplier => 1.03; + + public override IconUsage? Icon => FontAwesome.Solid.Fire; + + public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; + + [SettingSource("Beat divisor")] + public BindableFloat BeatDivisor { get; } = new BindableFloat(2) + { + MinValue = .25f, + MaxValue = 2, + Precision = .25F + }; public override void ApplyToBeatmap(IBeatmap beatmap) { base.ApplyToBeatmap(beatmap); - double lastObjectEnd = beatmap.HitObjects.LastOrDefault()?.GetEndTime() ?? 0; - foreach (var obj in beatmap.HitObjects.OfType()) - applyVariableAr(obj); - - void applyVariableAr(OsuHitObject osuObject) + foreach (var hitObject in beatmap.HitObjects.OfType()) { - double percentageofmap = osuObject.StartTime / lastObjectEnd; - osuObject.TimePreempt = (percentageofmap * osuObject.TimePreempt) * ARadded.Value + osuObject.TimePreempt; - foreach (var nested in osuObject.NestedHitObjects.OfType()) - applyVariableAr(nested); + hitObject.TimeFadeIn = hitObject.TimePreempt; + var point = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); + hitObject.TimePreempt += hitObject.StartTime % (point.BeatLength / BeatDivisor.Value); } } + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); + } + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { } - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyFrozenState(hitObject, state); + + private void applyFrozenState(DrawableHitObject drawable, ArmedState state) { + if (drawable is DrawableSpinner) + return; + + var h = (OsuHitObject)drawable.HitObject; + + switch (drawable) + { + case DrawableHitCircle circle: + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) + { + circle.ApproachCircle.Hide(); + } + + break; + } } - - [SettingSource("Additional AR", "how much AR change to add")] - public BindableDouble ARadded { get; } = new BindableDouble(1) - { - Precision = 0.1f, - MinValue = 0, - MaxValue = 10, - }; - - /*[SettingSource("Additional Ar", "how much ar change to add")] - public BindableBool scaledown { get; } = new BindableBool();*/ } } + diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 302194e91a..466b770399 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -198,7 +198,8 @@ namespace osu.Game.Rulesets.Osu new OsuModMuted(), new OsuModNoScope(), new MultiMod(new OsuModMagnetised(), new OsuModRepel()), - new ModAdaptiveSpeed() + new ModAdaptiveSpeed(), + new OsuModFlash() }; case ModType.System: From 7f08de522d68efd5d79e7b08d1d64e1ed81454f3 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Thu, 8 Sep 2022 01:21:03 +0200 Subject: [PATCH 016/321] Fixed --- osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs index b50de12465..e1ce43d875 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -10,7 +11,6 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; - public override string Description => "Burn the notes into your memory"; + public override LocalisableString Description => "Burn the notes into your memory"; public override ModType Type => ModType.Fun; @@ -31,22 +31,29 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; [SettingSource("Beat divisor")] - public BindableFloat BeatDivisor { get; } = new BindableFloat(2) + public BindableFloat BeatDivisor { get; } = new BindableFloat(1) { MinValue = .25f, - MaxValue = 2, - Precision = .25F + MaxValue = 5, + Precision = .25f }; public override void ApplyToBeatmap(IBeatmap beatmap) { base.ApplyToBeatmap(beatmap); - foreach (var hitObject in beatmap.HitObjects.OfType()) + foreach (var obj in beatmap.HitObjects.OfType()) { - hitObject.TimeFadeIn = hitObject.TimePreempt; - var point = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); - hitObject.TimePreempt += hitObject.StartTime % (point.BeatLength / BeatDivisor.Value); + applyFadeInAdjustment(obj); + var point = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); + obj.TimePreempt += obj.StartTime % (point.BeatLength * BeatDivisor.Value); + } + + static void applyFadeInAdjustment(OsuHitObject osuObject) + { + osuObject.TimeFadeIn = osuObject.TimePreempt; + foreach (var nested in osuObject.NestedHitObjects.OfType()) + applyFadeInAdjustment(nested); } } From 4a6c8785af7bd24b5f2e4a1be9e85c19624ac4c8 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Thu, 8 Sep 2022 11:14:56 +0200 Subject: [PATCH 017/321] Clean up --- osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs | 31 ++++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs index e1ce43d875..818eb93bfb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs @@ -38,30 +38,31 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = .25f }; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); + } + public override void ApplyToBeatmap(IBeatmap beatmap) { base.ApplyToBeatmap(beatmap); foreach (var obj in beatmap.HitObjects.OfType()) { - applyFadeInAdjustment(obj); var point = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); - obj.TimePreempt += obj.StartTime % (point.BeatLength * BeatDivisor.Value); - } + double val = obj.TimePreempt + obj.StartTime % (point.BeatLength * BeatDivisor.Value); + applyFadeInAdjustment(obj); - static void applyFadeInAdjustment(OsuHitObject osuObject) - { - osuObject.TimeFadeIn = osuObject.TimePreempt; - foreach (var nested in osuObject.NestedHitObjects.OfType()) - applyFadeInAdjustment(nested); + void applyFadeInAdjustment(OsuHitObject osuObject) + { + osuObject.TimePreempt = val; + foreach (var nested in osuObject.NestedHitObjects.OfType()) + applyFadeInAdjustment(nested); + } } } - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); - } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { } @@ -74,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Mods return; var h = (OsuHitObject)drawable.HitObject; - + /* switch (drawable) { case DrawableHitCircle circle: @@ -84,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Mods } break; - } + }*/ } } } From e416c87970de92b0b10e27733c2509bfacdf215c Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Thu, 8 Sep 2022 15:17:22 +0200 Subject: [PATCH 018/321] readded approach circles --- .../{OsuModFlash.cs => OsuModFreezeFrame.cs} | 23 +++---------------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 2 files changed, 4 insertions(+), 21 deletions(-) rename osu.Game.Rulesets.Osu/Mods/{OsuModFlash.cs => OsuModFreezeFrame.cs} (76%) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs similarity index 76% rename from osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 818eb93bfb..b6b6f4e588 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlash.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -8,13 +8,12 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFlash : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset + public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset { public override string Name => "Freeze frame"; @@ -26,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; - public override IconUsage? Icon => FontAwesome.Solid.Fire; + public override IconUsage? Icon => FontAwesome.Solid.Camera; public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; @@ -38,7 +37,6 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = .25f }; - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); @@ -69,23 +67,8 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyFrozenState(hitObject, state); - private void applyFrozenState(DrawableHitObject drawable, ArmedState state) + private void applyFrozenState(DrawableHitObject drawableObject, ArmedState state) { - if (drawable is DrawableSpinner) - return; - - var h = (OsuHitObject)drawable.HitObject; - /* - switch (drawable) - { - case DrawableHitCircle circle: - using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt)) - { - circle.ApproachCircle.Hide(); - } - - break; - }*/ } } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6fdf4c3bf8..2b42dc7eef 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets.Osu new OsuModNoScope(), new MultiMod(new OsuModMagnetised(), new OsuModRepel()), new ModAdaptiveSpeed(), - new OsuModFlash() + new OsuModFreezeFrame() }; case ModType.System: From cb17fb2091b351c0bf79f31a0818923529d2b274 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 9 Sep 2022 23:13:19 +0200 Subject: [PATCH 019/321] Create Test Scene --- .../Mods/TestSceneOsuModFreezeFrame.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs new file mode 100644 index 0000000000..4642229436 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModFreezeFrame : OsuModTestScene + { + [TestCase(0.5f)] + [TestCase(1)] + [TestCase(2)] + public void TestFreezeFrequency(float beatMeasure) + { + CreateModTest(new ModTestData + { + Mod = new OsuModFreezeFrame + { + BeatDivisor = { Value = beatMeasure } + }, + PassCondition = () => true, + Autoplay = true + }); + } + } +} From 23d435bc427c8d1e2780a46d500b77eff084c9df Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Sat, 10 Sep 2022 14:08:04 +0200 Subject: [PATCH 020/321] name changes --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index b6b6f4e588..6bbe01063b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -49,12 +49,12 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var obj in beatmap.HitObjects.OfType()) { var point = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); - double val = obj.TimePreempt + obj.StartTime % (point.BeatLength * BeatDivisor.Value); + double newPreempt = obj.TimePreempt + (obj.StartTime +5) % (point.BeatLength * BeatDivisor.Value); applyFadeInAdjustment(obj); void applyFadeInAdjustment(OsuHitObject osuObject) { - osuObject.TimePreempt = val; + osuObject.TimePreempt = newPreempt; foreach (var nested in osuObject.NestedHitObjects.OfType()) applyFadeInAdjustment(nested); } From eb84c513e34a682914322ee94bf78b6836e64e1a Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 02:14:14 +0200 Subject: [PATCH 021/321] Add ability to adjust to BPM changes fix first hitcircle being off time Bpm changes not working fix --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 6bbe01063b..84e41810ab 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override IconUsage? Icon => FontAwesome.Solid.Camera; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; [SettingSource("Beat divisor")] @@ -48,13 +47,17 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var obj in beatmap.HitObjects.OfType()) { - var point = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); - double newPreempt = obj.TimePreempt + (obj.StartTime +5) % (point.BeatLength * BeatDivisor.Value); + var lastTimingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime + 1); + double controlPointDifference = obj.StartTime + 1 - lastTimingPoint.Time; + double remainder = controlPointDifference % (lastTimingPoint.BeatLength * BeatDivisor.Value); + + double finalPreempt = obj.TimePreempt + remainder; + obj.TimePreempt = finalPreempt; applyFadeInAdjustment(obj); void applyFadeInAdjustment(OsuHitObject osuObject) { - osuObject.TimePreempt = newPreempt; + osuObject.TimePreempt = finalPreempt; foreach (var nested in osuObject.NestedHitObjects.OfType()) applyFadeInAdjustment(nested); } From 5a9b027ebcb32ecb44f6985ea86813682b5202f9 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 02:57:21 +0200 Subject: [PATCH 022/321] Use Enum for Settings --- .../Mods/OsuModFreezeFrame.cs | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 84e41810ab..42c4a8fa20 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -29,12 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; [SettingSource("Beat divisor")] - public BindableFloat BeatDivisor { get; } = new BindableFloat(1) - { - MinValue = .25f, - MaxValue = 5, - Precision = .25f - }; + public Bindable Divisor { get; } = new Bindable(BeatDivisor.Measure); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { @@ -48,11 +43,11 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var obj in beatmap.HitObjects.OfType()) { var lastTimingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime + 1); + // +1 is added due to First HitCircle in each measure not appearing appropriately without it double controlPointDifference = obj.StartTime + 1 - lastTimingPoint.Time; - double remainder = controlPointDifference % (lastTimingPoint.BeatLength * BeatDivisor.Value); + double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)); double finalPreempt = obj.TimePreempt + remainder; - obj.TimePreempt = finalPreempt; applyFadeInAdjustment(obj); void applyFadeInAdjustment(OsuHitObject osuObject) @@ -64,14 +59,41 @@ namespace osu.Game.Rulesets.Osu.Mods } } - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { } + + private float getMeasure(BeatDivisor divisor) { + switch (divisor) + { + case BeatDivisor.Quarter_Measure: + return 0.25f; + + case BeatDivisor.Half_Measure: + return 0.5f; + + case BeatDivisor.Measure: + return 1; + + case BeatDivisor.Double_Measure: + return 2; + + case BeatDivisor.Quadruple_Measure: + return 4; + + default: + throw new ArgumentOutOfRangeException(nameof(divisor), divisor, null); + } } - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyFrozenState(hitObject, state); - - private void applyFrozenState(DrawableHitObject drawableObject, ArmedState state) + public enum BeatDivisor { + Quarter_Measure, + Half_Measure, + Measure, + Double_Measure, + Quadruple_Measure } } } From 7fc0366afd56bfea4fcc91c45fc14124924218a6 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 13:18:38 +0200 Subject: [PATCH 023/321] Improve Tests Fix divisor in test --- .../Mods/TestSceneOsuModFreezeFrame.cs | 31 +++++++++++++------ .../Mods/OsuModFreezeFrame.cs | 17 +++++----- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs index 4642229436..8af63aa5e2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs @@ -1,27 +1,40 @@ // 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 NUnit.Framework; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModFreezeFrame : OsuModTestScene { - [TestCase(0.5f)] - [TestCase(1)] - [TestCase(2)] - public void TestFreezeFrequency(float beatMeasure) + [TestCase(OsuModFreezeFrame.BeatDivisor.Quarter_Measure)] + [TestCase(OsuModFreezeFrame.BeatDivisor.Single_Measure)] + [TestCase(OsuModFreezeFrame.BeatDivisor.Quadruple_Measure)] + public void TestFreezeFrequency(OsuModFreezeFrame.BeatDivisor divisor) { CreateModTest(new ModTestData { - Mod = new OsuModFreezeFrame - { - BeatDivisor = { Value = beatMeasure } - }, - PassCondition = () => true, + Mod = new OsuModFreezeFrame { Divisor = { Value = divisor } }, + PassCondition = checkSomeHit, Autoplay = true }); } + + [Test] + public void TestWithHidden() + { + var mods = new List { new OsuModHidden(), new OsuModFreezeFrame { Divisor = { Value = OsuModFreezeFrame.BeatDivisor.Quadruple_Measure } } }; + CreateModTest(new ModTestData + { + Mods = mods, + PassCondition = checkSomeHit, + Autoplay = true + }); + } + + private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 8; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 42c4a8fa20..08a8a85740 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset { - public override string Name => "Freeze frame"; + public override string Name => "Freeze Frame"; public override string Acronym => "FF"; @@ -26,10 +26,9 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override IconUsage? Icon => FontAwesome.Solid.Camera; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; - [SettingSource("Beat divisor")] - public Bindable Divisor { get; } = new Bindable(BeatDivisor.Measure); + [SettingSource("Measure", "How often the hitcircles should be Grouped to freeze")] + public Bindable Divisor { get; } = new Bindable(BeatDivisor.Single_Measure); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { @@ -42,11 +41,10 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var obj in beatmap.HitObjects.OfType()) { + // The +1s below are added due to First HitCircle in each measure not appearing appropriately without them. var lastTimingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime + 1); - // +1 is added due to First HitCircle in each measure not appearing appropriately without it double controlPointDifference = obj.StartTime + 1 - lastTimingPoint.Time; - double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)); - + double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)) - 1; double finalPreempt = obj.TimePreempt + remainder; applyFadeInAdjustment(obj); @@ -73,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Mods case BeatDivisor.Half_Measure: return 0.5f; - case BeatDivisor.Measure: + case BeatDivisor.Single_Measure: return 1; case BeatDivisor.Double_Measure: @@ -87,11 +85,12 @@ namespace osu.Game.Rulesets.Osu.Mods } } + //Todo: find better way to represent these Enums to the player public enum BeatDivisor { Quarter_Measure, Half_Measure, - Measure, + Single_Measure, Double_Measure, Quadruple_Measure } From 427bd182697cf40af987cd09526f643213752520 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 13:36:04 +0200 Subject: [PATCH 024/321] Add Copyright header --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 08a8a85740..6b29c511ec 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using System.Linq; using osu.Framework.Bindables; @@ -27,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => FontAwesome.Solid.Camera; - [SettingSource("Measure", "How often the hitcircles should be Grouped to freeze")] + [SettingSource("Measure", "How often the hit-circles should be Grouped to freeze")] public Bindable Divisor { get; } = new Bindable(BeatDivisor.Single_Measure); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) From 6db8f51c02fd7d96e67870951a02ded4eb7c865b Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 13:45:18 +0200 Subject: [PATCH 025/321] Improve Tests Fix divisor in test Add Copyright header --- .../Mods/TestSceneOsuModFreezeFrame.cs | 31 +++++++++++++------ .../Mods/OsuModFreezeFrame.cs | 20 ++++++------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs index 4642229436..8af63aa5e2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs @@ -1,27 +1,40 @@ // 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 NUnit.Framework; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests.Mods { public class TestSceneOsuModFreezeFrame : OsuModTestScene { - [TestCase(0.5f)] - [TestCase(1)] - [TestCase(2)] - public void TestFreezeFrequency(float beatMeasure) + [TestCase(OsuModFreezeFrame.BeatDivisor.Quarter_Measure)] + [TestCase(OsuModFreezeFrame.BeatDivisor.Single_Measure)] + [TestCase(OsuModFreezeFrame.BeatDivisor.Quadruple_Measure)] + public void TestFreezeFrequency(OsuModFreezeFrame.BeatDivisor divisor) { CreateModTest(new ModTestData { - Mod = new OsuModFreezeFrame - { - BeatDivisor = { Value = beatMeasure } - }, - PassCondition = () => true, + Mod = new OsuModFreezeFrame { Divisor = { Value = divisor } }, + PassCondition = checkSomeHit, Autoplay = true }); } + + [Test] + public void TestWithHidden() + { + var mods = new List { new OsuModHidden(), new OsuModFreezeFrame { Divisor = { Value = OsuModFreezeFrame.BeatDivisor.Quadruple_Measure } } }; + CreateModTest(new ModTestData + { + Mods = mods, + PassCondition = checkSomeHit, + Autoplay = true + }); + } + + private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 8; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 42c4a8fa20..6b29c511ec 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using System.Linq; using osu.Framework.Bindables; @@ -15,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset { - public override string Name => "Freeze frame"; + public override string Name => "Freeze Frame"; public override string Acronym => "FF"; @@ -26,10 +29,9 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override IconUsage? Icon => FontAwesome.Solid.Camera; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTarget), typeof(OsuModStrictTracking) }; - [SettingSource("Beat divisor")] - public Bindable Divisor { get; } = new Bindable(BeatDivisor.Measure); + [SettingSource("Measure", "How often the hit-circles should be Grouped to freeze")] + public Bindable Divisor { get; } = new Bindable(BeatDivisor.Single_Measure); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { @@ -42,11 +44,10 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var obj in beatmap.HitObjects.OfType()) { + // The +1s below are added due to First HitCircle in each measure not appearing appropriately without them. var lastTimingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime + 1); - // +1 is added due to First HitCircle in each measure not appearing appropriately without it double controlPointDifference = obj.StartTime + 1 - lastTimingPoint.Time; - double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)); - + double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)) - 1; double finalPreempt = obj.TimePreempt + remainder; applyFadeInAdjustment(obj); @@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Mods case BeatDivisor.Half_Measure: return 0.5f; - case BeatDivisor.Measure: + case BeatDivisor.Single_Measure: return 1; case BeatDivisor.Double_Measure: @@ -87,11 +88,12 @@ namespace osu.Game.Rulesets.Osu.Mods } } + //Todo: find better way to represent these Enums to the player public enum BeatDivisor { Quarter_Measure, Half_Measure, - Measure, + Single_Measure, Double_Measure, Quadruple_Measure } From a0c493656f7e738bb0fe5d52d00308ebc4310ab2 Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Fri, 16 Sep 2022 15:27:36 +0200 Subject: [PATCH 026/321] Remove Incompatibility with Approach Circle requiring mods --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 6b29c511ec..6a0faf9298 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -16,7 +16,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IHidesApproachCircles, IApplicableToDrawableRuleset + public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset { public override string Name => "Freeze Frame"; From 6d3e42a248021605150fbda3a7cfb8d9a4b98dd4 Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Sun, 18 Sep 2022 19:35:12 +0200 Subject: [PATCH 027/321] Give enums descriptions, change acronym and remove icon --- .../Mods/OsuModFreezeFrame.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 6a0faf9298..26adc0f25f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -3,8 +3,8 @@ using System; using System.Linq; +using System.ComponentModel; using osu.Framework.Bindables; -using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -20,17 +20,15 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Freeze Frame"; - public override string Acronym => "FF"; + public override string Acronym => "FR"; public override double ScoreMultiplier => 1; - public override LocalisableString Description => "Burn the notes into your memory"; + public override LocalisableString Description => "Burn the notes into your memory."; public override ModType Type => ModType.Fun; - public override IconUsage? Icon => FontAwesome.Solid.Camera; - - [SettingSource("Measure", "How often the hit-circles should be Grouped to freeze")] + [SettingSource("Beat Divisor", "How often the hitobjects should be grouped according to BPM")] public Bindable Divisor { get; } = new Bindable(BeatDivisor.Single_Measure); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -88,15 +86,22 @@ namespace osu.Game.Rulesets.Osu.Mods } } - //Todo: find better way to represent these Enums to the player public enum BeatDivisor { + [Description("1/4")] Quarter_Measure, + + [Description("1/2")] Half_Measure, + + [Description("1")] Single_Measure, + + [Description("2")] Double_Measure, + + [Description("4")] Quadruple_Measure } } } - From 9c4ae768f16e3d7c0f4c7f57c78480eb92ce34d4 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 27 Sep 2022 20:08:32 +0900 Subject: [PATCH 028/321] show object in timeline before placed just BeginPlacement() --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index df1450bf77..08e7e57688 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -26,6 +26,11 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) }; } + protected override void LoadComplete() + { + base.LoadComplete(); + BeginPlacement(); + } protected override bool OnMouseDown(MouseDownEvent e) { From 344015a6e6bec291b9829007938354b0e1474325 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Tue, 27 Sep 2022 20:12:40 +0900 Subject: [PATCH 029/321] do not move timeline when EndPlacement --- .../Edit/TaikoHitObjectComposer.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index 1c1a5c325f..06c982397c 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -18,6 +19,16 @@ namespace osu.Game.Rulesets.Taiko.Edit { } + public override void EndPlacement(HitObject hitObject, bool commit) + { + EditorBeatmap.PlacementObject.Value = null; + + if (commit) + { + EditorBeatmap.Add(hitObject); + } + } + protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new HitCompositionTool(), From e8cd9a5d110d05d200a4c5c33e9fc01e48360552 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 17:26:03 +0900 Subject: [PATCH 030/321] Allow arbitrary height catch editor Useful because can see more notes at once. --- ...CatchEditorPlayfieldAdjustmentContainer.cs | 49 +++++++++++++++++++ .../Edit/CatchHitObjectComposer.cs | 5 +- .../Edit/DrawableCatchEditorRuleset.cs | 10 ++++ osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 6 +++ .../UI/DrawableCatchRuleset.cs | 4 +- 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfieldAdjustmentContainer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfieldAdjustmentContainer.cs new file mode 100644 index 0000000000..0a0f91c781 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchEditorPlayfieldAdjustmentContainer.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.UI; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class CatchEditorPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer + { + protected override Container Content => content; + private readonly Container content; + + public CatchEditorPlayfieldAdjustmentContainer() + { + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + Size = new Vector2(0.8f, 0.9f); + + InternalChild = new ScalingContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Child = content = new Container { RelativeSizeAxes = Axes.Both }, + }; + } + + private class ScalingContainer : Container + { + public ScalingContainer() + { + RelativeSizeAxes = Axes.Y; + Width = CatchPlayfield.WIDTH; + } + + protected override void Update() + { + base.Update(); + + Scale = new Vector2(Math.Min(Parent.ChildSize.X / CatchPlayfield.WIDTH, Parent.ChildSize.Y / CatchPlayfield.HEIGHT)); + Height = 1 / Scale.Y; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index f31dc3ef9c..6a32de1995 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -51,7 +51,10 @@ namespace osu.Game.Rulesets.Catch.Edit LayerBelowRuleset.Add(new PlayfieldBorder { - RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = CatchPlayfield.HEIGHT, PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } }); diff --git a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs index c81afafae5..4f62a912e0 100644 --- a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs +++ b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs @@ -18,6 +18,16 @@ namespace osu.Game.Rulesets.Catch.Edit { } + protected override void Update() + { + base.Update(); + + double gamePlayTimeRange = GetTimeRange(Beatmap.Difficulty.ApproachRate); + TimeRange.Value = gamePlayTimeRange * (Playfield.DrawHeight / CatchPlayfield.HEIGHT); + } + protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.Difficulty); + + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchEditorPlayfieldAdjustmentContainer(); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index dad22fbe69..ce000b0fad 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -23,6 +23,12 @@ namespace osu.Game.Rulesets.Catch.UI /// public const float WIDTH = 512; + /// + /// The height of the playfield. + /// This doesn't include the catcher area. + /// + public const float HEIGHT = 384; + /// /// The center position of the playfield. /// diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index ef2936ac94..a1fbe7df38 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.UI : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = GetTimeRange(beatmap.Difficulty.ApproachRate); } [BackgroundDependencyLoader] @@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Catch.UI KeyBindingInputManager.Add(new CatchTouchInputMapper()); } + protected double GetTimeRange(float approachRate) => IBeatmapDifficultyInfo.DifficultyRange(approachRate, 1800, 1200, 450); + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayRecorder CreateReplayRecorder(Score score) => new CatchReplayRecorder(score, (CatchPlayfield)Playfield); From 9247ff3e0a0e66248ca489b99eb3115cd8bd7da9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 6 Oct 2022 18:06:16 +0900 Subject: [PATCH 031/321] Allow changing scrolling speed in catch editor Scroll speed is not saved and doesn't affect gameplay. It is purely a feature for a better visualization. It is currently bind to scroll speed increase/decrease. Default F3/F4 crashes with editor shortcuts so it has to be changed. --- .../Edit/CatchHitObjectComposer.cs | 29 ++++++++++++++++++- .../Edit/DrawableCatchEditorRuleset.cs | 6 +++- .../Edit/DistancedHitObjectComposer.cs | 4 +-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 6a32de1995..bc49deedae 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -12,8 +12,10 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; @@ -37,6 +39,12 @@ namespace osu.Game.Rulesets.Catch.Edit private InputManager inputManager; + private readonly BindableDouble timeRangeMultiplier = new BindableDouble(1) + { + MinValue = 1, + MaxValue = 10, + }; + public CatchHitObjectComposer(CatchRuleset ruleset) : base(ruleset) { @@ -80,8 +88,27 @@ namespace osu.Game.Rulesets.Catch.Edit updateDistanceSnapGrid(); } + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.IncreaseScrollSpeed: + this.TransformBindableTo(timeRangeMultiplier, timeRangeMultiplier.Value - 1, 200, Easing.OutQuint); + break; + + case GlobalAction.DecreaseScrollSpeed: + this.TransformBindableTo(timeRangeMultiplier, timeRangeMultiplier.Value + 1, 200, Easing.OutQuint); + break; + } + + return base.OnPressed(e); + } + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) => - new DrawableCatchEditorRuleset(ruleset, beatmap, mods); + new DrawableCatchEditorRuleset(ruleset, beatmap, mods) + { + TimeRangeMultiplier = { BindTarget = timeRangeMultiplier, } + }; protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { diff --git a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs index 4f62a912e0..67238f66d4 100644 --- a/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs +++ b/osu.Game.Rulesets.Catch/Edit/DrawableCatchEditorRuleset.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; @@ -13,6 +14,8 @@ namespace osu.Game.Rulesets.Catch.Edit { public class DrawableCatchEditorRuleset : DrawableCatchRuleset { + public readonly BindableDouble TimeRangeMultiplier = new BindableDouble(1); + public DrawableCatchEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { @@ -23,7 +26,8 @@ namespace osu.Game.Rulesets.Catch.Edit base.Update(); double gamePlayTimeRange = GetTimeRange(Beatmap.Difficulty.ApproachRate); - TimeRange.Value = gamePlayTimeRange * (Playfield.DrawHeight / CatchPlayfield.HEIGHT); + float playfieldStretch = Playfield.DrawHeight / CatchPlayfield.HEIGHT; + TimeRange.Value = gamePlayTimeRange * TimeRangeMultiplier.Value * playfieldStretch; } protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.Difficulty); diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 4726211666..3246f6e352 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Edit } } - public bool OnPressed(KeyBindingPressEvent e) + public virtual bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Edit return false; } - public void OnReleased(KeyBindingReleaseEvent e) + public virtual void OnReleased(KeyBindingReleaseEvent e) { } From e0904e263f4c976e24277c19f380650fb025499f Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Sun, 9 Oct 2022 11:25:47 +0200 Subject: [PATCH 032/321] Fix legacy BpmMultiplier clamp value --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 9c066ada08..360a91a7e5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats : this() { // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; GenerateTicks = !double.IsNaN(beatLength); } From f8036658c701e3f85b808c1b121ec9f35d50b0f0 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Mon, 10 Oct 2022 09:36:18 +0200 Subject: [PATCH 033/321] Move clamps directly to the usages in the conversion algorithm --- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 2bdd0e16ad..89b52a3c75 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; + beatLength = timingPoint.BeatLength * Math.Min(legacyDifficultyPoint.BpmMultiplier, 10); else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 524565a863..33f1535437 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; + beatLength = timingPoint.BeatLength * Math.Min(legacyDifficultyPoint.BpmMultiplier, 10); else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 360a91a7e5..9c066ada08 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats : this() { // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; GenerateTicks = !double.IsNaN(beatLength); } From 9b45a9cf76c4e50c8f821b495e8cb39715dc7550 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 11 Oct 2022 16:20:57 +0300 Subject: [PATCH 034/321] Rename various cursor symbols - `IProvideCursor.MenuCursor` -> `IProvideCursor.Cursor` - `MenuCursor` -> `MenuCursorContainer --- .../TestSceneZoomableScrollContainer.cs | 2 +- .../Visual/UserInterface/TestSceneCursors.cs | 50 +++++++++---------- .../Graphics/Cursor/GlobalCursorDisplay.cs | 16 +++--- osu.Game/Graphics/Cursor/IProvideCursor.cs | 4 +- .../{MenuCursor.cs => MenuCursorContainer.cs} | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- osu.Game/Screens/Utility/LatencyArea.cs | 8 +-- 7 files changed, 43 insertions(+), 41 deletions(-) rename osu.Game/Graphics/Cursor/{MenuCursor.cs => MenuCursorContainer.cs} (99%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs index 1858aee76b..89c5b9b23b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneZoomableScrollContainer.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Editing } } }, - new MenuCursor() + new MenuCursorContainer() }; scrollContainer.Add(innerBox = new Box diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index bcbe146456..6033fc5871 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -96,10 +96,10 @@ namespace osu.Game.Tests.Visual.UserInterface private void testUserCursor() { AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); - AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].MenuCursor)); - AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].MenuCursor)); + AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddStep("Move out", moveOut); - AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].MenuCursor)); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor)); } @@ -111,12 +111,12 @@ namespace osu.Game.Tests.Visual.UserInterface private void testLocalCursor() { AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor)); - AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].MenuCursor)); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor)); AddAssert("Check global cursor at mouse", () => checkAtMouse(globalCursorDisplay.MenuCursor)); AddStep("Move out", moveOut); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor)); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor)); } @@ -128,12 +128,12 @@ namespace osu.Game.Tests.Visual.UserInterface private void testUserCursorOverride() { AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); - AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].MenuCursor)); - AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].MenuCursor)); - AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].MenuCursor)); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddStep("Move out", moveOut); - AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].MenuCursor)); - AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].MenuCursor)); + AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); } /// @@ -143,13 +143,13 @@ namespace osu.Game.Tests.Visual.UserInterface private void testMultipleLocalCursors() { AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor)); - AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].MenuCursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor)); - AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].MenuCursor)); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); AddStep("Move out", moveOut); - AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor)); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); } /// @@ -159,13 +159,13 @@ namespace osu.Game.Tests.Visual.UserInterface private void testUserOverrideWithLocal() { AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); - AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].MenuCursor)); - AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].MenuCursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor)); - AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].MenuCursor)); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); AddStep("Move out", moveOut); - AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].MenuCursor)); - AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor)); + AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); } /// @@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.UserInterface { public bool SmoothTransition; - public CursorContainer MenuCursor { get; } + public CursorContainer Cursor { get; } public bool ProvidingUserCursor { get; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor); @@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.Centre, Text = providesUserCursor ? "User cursor" : "Local cursor" }, - MenuCursor = new TestCursorContainer + Cursor = new TestCursorContainer { State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible }, } diff --git a/osu.Game/Graphics/Cursor/GlobalCursorDisplay.cs b/osu.Game/Graphics/Cursor/GlobalCursorDisplay.cs index 6613e18cbe..f5429723be 100644 --- a/osu.Game/Graphics/Cursor/GlobalCursorDisplay.cs +++ b/osu.Game/Graphics/Cursor/GlobalCursorDisplay.cs @@ -13,7 +13,7 @@ using osu.Game.Configuration; namespace osu.Game.Graphics.Cursor { /// - /// A container which provides the main . + /// A container which provides the main . /// Also handles cases where a more localised cursor is provided by another component (via ). /// public class GlobalCursorDisplay : Container, IProvideCursor @@ -23,7 +23,9 @@ namespace osu.Game.Graphics.Cursor /// internal bool ShowCursor = true; - public CursorContainer MenuCursor { get; } + CursorContainer IProvideCursor.Cursor => MenuCursor; + + public MenuCursorContainer MenuCursor { get; } public bool ProvidingUserCursor => true; @@ -42,8 +44,8 @@ namespace osu.Game.Graphics.Cursor { AddRangeInternal(new Drawable[] { - MenuCursor = new MenuCursor { State = { Value = Visibility.Hidden } }, - Content = new Container { RelativeSizeAxes = Axes.Both } + Content = new Container { RelativeSizeAxes = Axes.Both }, + MenuCursor = new MenuCursorContainer { State = { Value = Visibility.Hidden } } }); } @@ -64,7 +66,7 @@ namespace osu.Game.Graphics.Cursor if (!hasValidInput || !ShowCursor) { - currentOverrideProvider?.MenuCursor?.Hide(); + currentOverrideProvider?.Cursor?.Hide(); currentOverrideProvider = null; return; } @@ -83,8 +85,8 @@ namespace osu.Game.Graphics.Cursor if (currentOverrideProvider == newOverrideProvider) return; - currentOverrideProvider?.MenuCursor?.Hide(); - newOverrideProvider.MenuCursor?.Show(); + currentOverrideProvider?.Cursor?.Hide(); + newOverrideProvider.Cursor?.Show(); currentOverrideProvider = newOverrideProvider; } diff --git a/osu.Game/Graphics/Cursor/IProvideCursor.cs b/osu.Game/Graphics/Cursor/IProvideCursor.cs index f7f7b75bc8..9f01e5da6d 100644 --- a/osu.Game/Graphics/Cursor/IProvideCursor.cs +++ b/osu.Game/Graphics/Cursor/IProvideCursor.cs @@ -17,10 +17,10 @@ namespace osu.Game.Graphics.Cursor /// The cursor provided by this . /// May be null if no cursor should be visible. /// - CursorContainer MenuCursor { get; } + CursorContainer Cursor { get; } /// - /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. + /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays). /// bool ProvidingUserCursor { get; } diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs similarity index 99% rename from osu.Game/Graphics/Cursor/MenuCursor.cs rename to osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 862a10208c..53c78ad5a2 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -21,7 +21,7 @@ using osuTK; namespace osu.Game.Graphics.Cursor { - public class MenuCursor : CursorContainer + public class MenuCursorContainer : CursorContainer { private readonly IBindable screenshotCursorVisibility = new Bindable(true); public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent; diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 73acb1759f..bd10bc839c 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -384,7 +384,7 @@ namespace osu.Game.Rulesets.UI // only show the cursor when within the playfield, by default. public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Playfield.ReceivePositionalInputAt(screenSpacePos); - CursorContainer IProvideCursor.MenuCursor => Playfield.Cursor; + CursorContainer IProvideCursor.Cursor => Playfield.Cursor; public override GameplayCursorContainer Cursor => Playfield.Cursor; diff --git a/osu.Game/Screens/Utility/LatencyArea.cs b/osu.Game/Screens/Utility/LatencyArea.cs index c8e0bf93a2..b7d45ba642 100644 --- a/osu.Game/Screens/Utility/LatencyArea.cs +++ b/osu.Game/Screens/Utility/LatencyArea.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Utility public readonly Bindable VisualMode = new Bindable(); - public CursorContainer? MenuCursor { get; private set; } + public CursorContainer? Cursor { get; private set; } public bool ProvidingUserCursor => IsActiveArea.Value; @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - MenuCursor = new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, @@ -105,7 +105,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - MenuCursor = new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Utility { RelativeSizeAxes = Axes.Both, }, - MenuCursor = new LatencyCursorContainer + Cursor = new LatencyCursorContainer { RelativeSizeAxes = Axes.Both, }, From 894127a948db0e429265f7d9eaff581dd0a04fd8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 11 Oct 2022 16:21:50 +0300 Subject: [PATCH 035/321] Expose bindable for identifying last input source --- osu.Game/Input/OsuUserInputManager.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs index 7a9002a004..5e700e32de 100644 --- a/osu.Game/Input/OsuUserInputManager.cs +++ b/osu.Game/Input/OsuUserInputManager.cs @@ -3,17 +3,43 @@ #nullable disable +using osu.Framework.Bindables; using osu.Framework.Input; +using osu.Framework.Input.StateChanges.Events; using osuTK.Input; namespace osu.Game.Input { public class OsuUserInputManager : UserInputManager { + /// + /// Whether the last input applied to the game is sourced from mouse. + /// + public IBindable IsMouseInputSource => isMouseInputSource; + + private readonly Bindable isMouseInputSource = new Bindable(); + internal OsuUserInputManager() { } + public override void HandleInputStateChange(InputStateChangeEvent inputStateChange) + { + switch (inputStateChange) + { + case ButtonStateChangeEvent: + case MousePositionChangeEvent: + isMouseInputSource.Value = true; + break; + + default: + isMouseInputSource.Value = false; + break; + } + + base.HandleInputStateChange(inputStateChange); + } + protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) { switch (button) From 60c92c874469d6006cc193ea70bfd24788ef1082 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 11 Oct 2022 16:22:57 +0300 Subject: [PATCH 036/321] Enable NRT on `MenuCursorContainer` --- .../Graphics/Cursor/MenuCursorContainer.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 53c78ad5a2..01c8853a04 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -28,17 +25,18 @@ namespace osu.Game.Graphics.Cursor protected override Drawable CreateCursor() => activeCursor = new Cursor(); - private Cursor activeCursor; + private Cursor activeCursor = null!; - private Bindable cursorRotate; private DragRotationState dragRotationState; private Vector2 positionMouseDown; - private Sample tapSample; private Vector2 lastMovePosition; - [BackgroundDependencyLoader(true)] - private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio) + private Bindable cursorRotate = null!; + private Sample tapSample = null!; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config, ScreenshotManager? screenshotManager, AudioManager audio) { cursorRotate = config.GetBindable(OsuSetting.CursorRotation); @@ -163,11 +161,11 @@ namespace osu.Game.Graphics.Cursor public class Cursor : Container { - private Container cursorContainer; - private Bindable cursorScale; + private Container cursorContainer = null!; + private Bindable cursorScale = null!; private const float base_scale = 0.15f; - public Sprite AdditiveLayer; + public Sprite AdditiveLayer = null!; public Cursor() { From 0a97ee71a97968dcddb1a8f423b28865a0ef6f67 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 11 Oct 2022 16:23:24 +0300 Subject: [PATCH 037/321] Hide menu cursor when non-mouse input is applied --- .../Graphics/Cursor/MenuCursorContainer.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 01c8853a04..70fa66b35a 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Input; using osuTK; namespace osu.Game.Graphics.Cursor @@ -27,14 +28,22 @@ namespace osu.Game.Graphics.Cursor private Cursor activeCursor = null!; + private readonly Container fadeContainer; + + protected override Container Content => fadeContainer; + private DragRotationState dragRotationState; private Vector2 positionMouseDown; - private Vector2 lastMovePosition; private Bindable cursorRotate = null!; private Sample tapSample = null!; + public MenuCursorContainer() + { + InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both }; + } + [BackgroundDependencyLoader] private void load(OsuConfigManager config, ScreenshotManager? screenshotManager, AudioManager audio) { @@ -46,6 +55,19 @@ namespace osu.Game.Graphics.Cursor tapSample = audio.Samples.Get(@"UI/cursor-tap"); } + [Resolved] + private OsuUserInputManager inputManager { get; set; } = null!; + + private IBindable mouseInputSource = null!; + + protected override void LoadComplete() + { + base.LoadComplete(); + + mouseInputSource = inputManager.IsMouseInputSource.GetBoundCopy(); + mouseInputSource.BindValueChanged(m => updateInternalVisibilityState(m.NewValue), true); + } + protected override void Update() { base.Update(); @@ -146,6 +168,8 @@ namespace osu.Game.Graphics.Cursor activeCursor.ScaleTo(0.6f, 250, Easing.In); } + private void updateInternalVisibilityState(bool show) => fadeContainer.FadeTo(show ? 1 : 0, 120, Easing.OutQuint); + private void playTapSample(double baseFrequency = 1f) { const float random_range = 0.02f; From 0a0d6993084ce9f2a9010eea9a00a45c2f03df31 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 11 Oct 2022 22:43:47 +0200 Subject: [PATCH 038/321] Convert slider velocity to scroll speed in taiko beatmap conversion --- .../Beatmaps/TaikoBeatmapConverter.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 524565a863..c1e1052569 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -57,6 +57,29 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Beatmap converted = base.ConvertBeatmap(original, cancellationToken); + if (original.BeatmapInfo.Ruleset.OnlineID == 0) + { + // Post processing step to transform standard slider velocity changes into scroll speed changes + double lastScrollSpeed = 1; + + foreach (HitObject hitObject in original.HitObjects) + { + double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity; + + if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed)) + { + EffectControlPoint currentControlPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime); + + if (Precision.AlmostEquals(currentControlPoint.Time, hitObject.StartTime)) + currentControlPoint.ScrollSpeed = nextScrollSpeed; + else + converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint { ScrollSpeed = nextScrollSpeed }); + + lastScrollSpeed = nextScrollSpeed; + } + } + } + if (original.BeatmapInfo.Ruleset.OnlineID == 3) { // Post processing step to transform mania hit objects with the same start time into strong hits From df0501235d21dee2e24c9801b099bf27a4e97725 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Wed, 12 Oct 2022 11:14:01 +0200 Subject: [PATCH 039/321] Introduce `BpmMultiplierMania`, remove after the fact clamps --- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 89b52a3c75..2bdd0e16ad 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - beatLength = timingPoint.BeatLength * Math.Min(legacyDifficultyPoint.BpmMultiplier, 10); + beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 33f1535437..524565a863 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - beatLength = timingPoint.BeatLength * Math.Min(legacyDifficultyPoint.BpmMultiplier, 10); + beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 9c066ada08..03819af149 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -168,6 +168,13 @@ namespace osu.Game.Beatmaps.Formats /// public double BpmMultiplier { get; private set; } + /// + /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. + /// This is to be used for taiko and mania specific beatmaps. + /// DO NOT USE THIS UNLESS 100% SURE. + /// + public double BpmMultiplierMania { get; private set; } + /// /// Whether or not slider ticks should be generated at this control point. /// This exists for backwards compatibility with maps that abuse NaN slider velocity behavior on osu!stable (e.g. /b/2628991). @@ -178,7 +185,8 @@ namespace osu.Game.Beatmaps.Formats : this() { // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; + BpmMultiplierMania = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; GenerateTicks = !double.IsNaN(beatLength); } @@ -196,6 +204,7 @@ namespace osu.Game.Beatmaps.Formats base.CopyFrom(other); BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier; + BpmMultiplierMania = ((LegacyDifficultyControlPoint)other).BpmMultiplierMania; GenerateTicks = ((LegacyDifficultyControlPoint)other).GenerateTicks; } @@ -206,10 +215,11 @@ namespace osu.Game.Beatmaps.Formats public bool Equals(LegacyDifficultyControlPoint? other) => base.Equals(other) && BpmMultiplier == other.BpmMultiplier + && BpmMultiplierMania == other.BpmMultiplierMania && GenerateTicks == other.GenerateTicks; // ReSharper disable twice NonReadonlyMemberInGetHashCode - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier, GenerateTicks); + public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier, BpmMultiplierMania, GenerateTicks); } internal class LegacySampleControlPoint : SampleControlPoint, IEquatable From 2579ed46ce552c3c610f5bacb8359e0f764616df Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Wed, 12 Oct 2022 11:22:54 +0200 Subject: [PATCH 040/321] Select multiplier per mode --- .../Beatmaps/TaikoBeatmapConverter.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 524565a863..613c41a59a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -162,7 +162,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; + { + double bpmMultiplier; + if (beatmap.BeatmapInfo.OnlineID == 1 || beatmap.BeatmapInfo.OnlineID == 3) + bpmMultiplier = legacyDifficultyPoint.BpmMultiplierMania; + else + bpmMultiplier = legacyDifficultyPoint.BpmMultiplier; + beatLength = timingPoint.BeatLength * bpmMultiplier; + } else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; From 83aedb193079a016f841298d2e4e68227d3fe58b Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Wed, 12 Oct 2022 12:42:26 +0200 Subject: [PATCH 041/321] Make mod use new Combo, remove pointless test --- .../Mods/TestSceneOsuModFreezeFrame.cs | 40 --------- .../Mods/OsuModFreezeFrame.cs | 89 +++++-------------- 2 files changed, 22 insertions(+), 107 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs deleted file mode 100644 index 8af63aa5e2..0000000000 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Rulesets.Osu.Tests.Mods -{ - public class TestSceneOsuModFreezeFrame : OsuModTestScene - { - [TestCase(OsuModFreezeFrame.BeatDivisor.Quarter_Measure)] - [TestCase(OsuModFreezeFrame.BeatDivisor.Single_Measure)] - [TestCase(OsuModFreezeFrame.BeatDivisor.Quadruple_Measure)] - public void TestFreezeFrequency(OsuModFreezeFrame.BeatDivisor divisor) - { - CreateModTest(new ModTestData - { - Mod = new OsuModFreezeFrame { Divisor = { Value = divisor } }, - PassCondition = checkSomeHit, - Autoplay = true - }); - } - - [Test] - public void TestWithHidden() - { - var mods = new List { new OsuModHidden(), new OsuModFreezeFrame { Divisor = { Value = OsuModFreezeFrame.BeatDivisor.Quadruple_Measure } } }; - CreateModTest(new ModTestData - { - Mods = mods, - PassCondition = checkSomeHit, - Autoplay = true - }); - } - - private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 8; - } -} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 26adc0f25f..b21e8a4a5f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -1,22 +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; using System.Linq; -using System.ComponentModel; -using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFreezeFrame : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset + public class OsuModFreezeFrame : Mod, IApplicableToBeatmap, IApplicableToDrawableRuleset { public override string Name => "Freeze Frame"; @@ -28,80 +23,40 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; - [SettingSource("Beat Divisor", "How often the hitobjects should be grouped according to BPM")] - public Bindable Divisor { get; } = new Bindable(BeatDivisor.Single_Measure); - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); } - public override void ApplyToBeatmap(IBeatmap beatmap) + public void ApplyToBeatmap(IBeatmap beatmap) { - base.ApplyToBeatmap(beatmap); + double lastNewComboTime = 0; foreach (var obj in beatmap.HitObjects.OfType()) { - // The +1s below are added due to First HitCircle in each measure not appearing appropriately without them. - var lastTimingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime + 1); - double controlPointDifference = obj.StartTime + 1 - lastTimingPoint.Time; - double remainder = controlPointDifference % (lastTimingPoint.BeatLength * getMeasure(Divisor.Value)) - 1; - double finalPreempt = obj.TimePreempt + remainder; - applyFadeInAdjustment(obj); + if (obj.NewCombo) { lastNewComboTime = obj.StartTime; } - void applyFadeInAdjustment(OsuHitObject osuObject) + applyFadeInAdjustment(obj); + } + + void applyFadeInAdjustment(OsuHitObject osuObject) + { + osuObject.TimePreempt += osuObject.StartTime - lastNewComboTime; + + foreach (var nested in osuObject.NestedHitObjects.OfType()) { - osuObject.TimePreempt = finalPreempt; - foreach (var nested in osuObject.NestedHitObjects.OfType()) - applyFadeInAdjustment(nested); + switch (nested) + { + //SliderRepeat wont layer correctly if preempt is changed. + case SliderRepeat: + return; + + default: + applyFadeInAdjustment(nested); + break; + } } } } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { } - - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { } - - private float getMeasure(BeatDivisor divisor) - { - switch (divisor) - { - case BeatDivisor.Quarter_Measure: - return 0.25f; - - case BeatDivisor.Half_Measure: - return 0.5f; - - case BeatDivisor.Single_Measure: - return 1; - - case BeatDivisor.Double_Measure: - return 2; - - case BeatDivisor.Quadruple_Measure: - return 4; - - default: - throw new ArgumentOutOfRangeException(nameof(divisor), divisor, null); - } - } - - public enum BeatDivisor - { - [Description("1/4")] - Quarter_Measure, - - [Description("1/2")] - Half_Measure, - - [Description("1")] - Single_Measure, - - [Description("2")] - Double_Measure, - - [Description("4")] - Quadruple_Measure - } } } From 8305d886e6460b37818b1eb28a8106d8bd1a6e77 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 Oct 2022 17:50:04 +0300 Subject: [PATCH 042/321] Refactor internal visibility logic to route back to `PopIn`/`PopOut` --- .../Graphics/Cursor/MenuCursorContainer.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 70fa66b35a..9f876a5e2d 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -28,10 +28,6 @@ namespace osu.Game.Graphics.Cursor private Cursor activeCursor = null!; - private readonly Container fadeContainer; - - protected override Container Content => fadeContainer; - private DragRotationState dragRotationState; private Vector2 positionMouseDown; private Vector2 lastMovePosition; @@ -39,11 +35,6 @@ namespace osu.Game.Graphics.Cursor private Bindable cursorRotate = null!; private Sample tapSample = null!; - public MenuCursorContainer() - { - InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both }; - } - [BackgroundDependencyLoader] private void load(OsuConfigManager config, ScreenshotManager? screenshotManager, AudioManager audio) { @@ -58,14 +49,37 @@ namespace osu.Game.Graphics.Cursor [Resolved] private OsuUserInputManager inputManager { get; set; } = null!; - private IBindable mouseInputSource = null!; + private readonly IBindable mouseInputSource = new BindableBool(); + + private readonly Bindable internalState = new Bindable(); protected override void LoadComplete() { base.LoadComplete(); - mouseInputSource = inputManager.IsMouseInputSource.GetBoundCopy(); - mouseInputSource.BindValueChanged(m => updateInternalVisibilityState(m.NewValue), true); + internalState.ValueChanged += onInternalStateChanged; + + mouseInputSource.BindTo(inputManager.IsMouseInputSource); + mouseInputSource.BindValueChanged(_ => updateInternalVisibility(), true); + + } + + private void updateInternalVisibility() + { + bool visible = mouseInputSource.Value; + internalState.Value = visible ? Visibility.Visible : Visibility.Hidden; + } + + private void onInternalStateChanged(ValueChangedEvent internalState) + { + if (State.Value == Visibility.Visible) + base.UpdateState(internalState); + } + + protected override void UpdateState(ValueChangedEvent state) + { + if (internalState.Value == Visibility.Visible) + base.UpdateState(state); } protected override void Update() @@ -168,8 +182,6 @@ namespace osu.Game.Graphics.Cursor activeCursor.ScaleTo(0.6f, 250, Easing.In); } - private void updateInternalVisibilityState(bool show) => fadeContainer.FadeTo(show ? 1 : 0, 120, Easing.OutQuint); - private void playTapSample(double baseFrequency = 1f) { const float random_range = 0.02f; From a44cfe29013a6b92eefdc20766257e0422c0ad2a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 Oct 2022 17:50:31 +0300 Subject: [PATCH 043/321] Hide menu cursor when user is idle --- osu.Game/Graphics/Cursor/MenuCursorContainer.cs | 9 +++++++++ osu.Game/OsuGame.cs | 8 ++++++-- osu.Game/OsuGameBase.cs | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 9f876a5e2d..cc747f00d5 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -49,7 +49,11 @@ namespace osu.Game.Graphics.Cursor [Resolved] private OsuUserInputManager inputManager { get; set; } = null!; + [Resolved] + private OsuGame? game { get; set; } + private readonly IBindable mouseInputSource = new BindableBool(); + private readonly IBindable isIdle = new BindableBool(); private readonly Bindable internalState = new Bindable(); @@ -62,6 +66,11 @@ namespace osu.Game.Graphics.Cursor mouseInputSource.BindTo(inputManager.IsMouseInputSource); mouseInputSource.BindValueChanged(_ => updateInternalVisibility(), true); + if (game != null) + { + isIdle.BindTo(game.IsIdle); + isIdle.BindValueChanged(_ => updateInternalVisibility()); + } } private void updateInternalVisibility() diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1716e48395..b3eaf5cd01 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -70,6 +70,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// + [Cached(typeof(OsuGame))] public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler { /// @@ -136,6 +137,11 @@ namespace osu.Game private IdleTracker idleTracker; + /// + /// Whether the user is currently in an idle state. + /// + public IBindable IsIdle => idleTracker.IsIdle; + /// /// Whether overlays should be able to be opened game-wide. Value is sourced from the current active screen. /// @@ -266,8 +272,6 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { - dependencies.CacheAs(this); - SentryLogger.AttachUser(API.LocalUser); dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 478f154d58..7d9ed7bf3e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -62,6 +62,7 @@ namespace osu.Game /// Unlike , this class will not load any kind of UI, allowing it to be used /// for provide dependencies to test cases without interfering with them. /// + [Cached(typeof(OsuGameBase))] public partial class OsuGameBase : Framework.Game, ICanAcceptFiles, IBeatSyncProvider { public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv" }; @@ -253,7 +254,6 @@ namespace osu.Game largeStore.AddTextureSource(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); - dependencies.CacheAs(this); dependencies.CacheAs(LocalConfig); InitialiseFonts(); From 7a6ecaff0d14569a0beb64d7f0820c68ce954a39 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 Oct 2022 17:50:47 +0300 Subject: [PATCH 044/321] Remove default invisible cursor container from rulesets --- osu.Game/Rulesets/UI/Playfield.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 2ec72d8fe3..354f6c38b6 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.UI /// The default provided cursor is invisible when inside the bounds of the . /// /// The cursor, or null to show the menu cursor. - protected virtual GameplayCursorContainer CreateCursor() => new InvisibleCursorContainer(); + protected virtual GameplayCursorContainer CreateCursor() => null; /// /// Registers a as a nested . @@ -522,14 +522,5 @@ namespace osu.Game.Rulesets.UI } #endregion - - public class InvisibleCursorContainer : GameplayCursorContainer - { - protected override Drawable CreateCursor() => new InvisibleCursor(); - - private class InvisibleCursor : Drawable - { - } - } } } From 6199db11a2e9872bd688e74d608ea24ba5d77f2f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 Oct 2022 17:55:32 +0300 Subject: [PATCH 045/321] Make `OsuUserInputManager` dependency nullable for tournament client --- osu.Game/Graphics/Cursor/MenuCursorContainer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index cc747f00d5..380af577ba 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -47,7 +47,7 @@ namespace osu.Game.Graphics.Cursor } [Resolved] - private OsuUserInputManager inputManager { get; set; } = null!; + private OsuUserInputManager? inputManager { get; set; } [Resolved] private OsuGame? game { get; set; } @@ -63,8 +63,11 @@ namespace osu.Game.Graphics.Cursor internalState.ValueChanged += onInternalStateChanged; - mouseInputSource.BindTo(inputManager.IsMouseInputSource); - mouseInputSource.BindValueChanged(_ => updateInternalVisibility(), true); + if (inputManager != null) + { + mouseInputSource.BindTo(inputManager.IsMouseInputSource); + mouseInputSource.BindValueChanged(_ => updateInternalVisibility(), true); + } if (game != null) { From f67f6cc99ca914b152a355122589900437b6b91d Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Wed, 12 Oct 2022 19:45:08 +0200 Subject: [PATCH 046/321] Fix switch case --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index b21e8a4a5f..a91d7831c3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Mods { //SliderRepeat wont layer correctly if preempt is changed. case SliderRepeat: - return; + break; default: applyFadeInAdjustment(nested); From 4fb156ef4eb8055d84d14b1889b8137ec3928abc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 13 Oct 2022 00:16:42 +0300 Subject: [PATCH 047/321] Update xmlodc and add nullability attributes --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 1 + osu.Game/Rulesets/UI/Playfield.cs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index bd10bc839c..dd3a950264 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -499,6 +499,7 @@ namespace osu.Game.Rulesets.UI /// /// The cursor being displayed by the . May be null if no cursor is provided. /// + [CanBeNull] public abstract GameplayCursorContainer Cursor { get; } /// diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 354f6c38b6..e59e45722a 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -202,14 +202,12 @@ namespace osu.Game.Rulesets.UI /// /// The cursor currently being used by this . May be null if no cursor is provided. /// + [CanBeNull] public GameplayCursorContainer Cursor { get; private set; } /// /// Provide a cursor which is to be used for gameplay. /// - /// - /// The default provided cursor is invisible when inside the bounds of the . - /// /// The cursor, or null to show the menu cursor. protected virtual GameplayCursorContainer CreateCursor() => null; From 09cc89cfa0fa603673fc4cb1170883f7cdc51d00 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 13 Oct 2022 00:17:02 +0300 Subject: [PATCH 048/321] Update existing usages of `Cursor` to handle null value --- osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs | 14 +++++++++----- .../Mods/TestSceneOsuModNoScope.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 5 ++++- osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs index 69ae8328e9..3f7560844c 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModRelax.cs @@ -19,17 +19,20 @@ namespace osu.Game.Rulesets.Catch.Mods { public override LocalisableString Description => @"Use the mouse to control the catcher."; - private DrawableRuleset drawableRuleset = null!; + private DrawableCatchRuleset drawableRuleset = null!; public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - this.drawableRuleset = drawableRuleset; + this.drawableRuleset = (DrawableCatchRuleset)drawableRuleset; } public void ApplyToPlayer(Player player) { if (!drawableRuleset.HasReplayLoaded.Value) - drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); + { + var catchPlayfield = (CatchPlayfield)drawableRuleset.Playfield; + catchPlayfield.CatcherArea.Add(new MouseInputHelper(catchPlayfield.CatcherArea)); + } } private class MouseInputHelper : Drawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition @@ -38,9 +41,10 @@ namespace osu.Game.Rulesets.Catch.Mods public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - public MouseInputHelper(CatchPlayfield playfield) + public MouseInputHelper(CatcherArea catcherArea) { - catcherArea = playfield.CatcherArea; + this.catcherArea = catcherArea; + RelativeSizeAxes = Axes.Both; } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs index 44404ca245..045a7e32eb 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Utils; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -145,6 +146,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private bool isBreak() => Player.IsBreakTime.Value; - private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.Alpha, alpha, 0.1f); + private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.AsNonNull().Alpha, alpha, 0.1f); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index fbde9e0491..38d90eb121 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Timing; @@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - var cursorPos = playfield.Cursor.ActiveCursor.DrawPosition; + var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition; foreach (var drawable in playfield.HitObjectContainer.AliveObjects) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 2f84c30581..9a8aa57746 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -35,7 +36,9 @@ namespace osu.Game.Rulesets.Osu.Mods { bool shouldAlwaysShowCursor = IsBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); float targetAlpha = shouldAlwaysShowCursor ? 1 : ComboBasedAlpha; - playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / TRANSITION_DURATION, 0, 1)); + + var cursor = playfield.Cursor.AsNonNull(); + cursor.Alpha = (float)Interpolation.Lerp(cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / TRANSITION_DURATION, 0, 1)); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs index 911363a27e..31a6b69d6b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Localisation; using osu.Framework.Timing; using osu.Framework.Utils; @@ -45,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - var cursorPos = playfield.Cursor.ActiveCursor.DrawPosition; + var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition; foreach (var drawable in playfield.HitObjectContainer.AliveObjects) { From e240f659c21e11a787c9a50b1d638de88b75fd1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 11:22:40 +0900 Subject: [PATCH 049/321] Refactor visibility states to read better --- .../Graphics/Cursor/MenuCursorContainer.cs | 39 +++++++++---------- osu.Game/Input/OsuUserInputManager.cs | 8 ++-- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 380af577ba..94bdef9842 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -35,6 +35,8 @@ namespace osu.Game.Graphics.Cursor private Bindable cursorRotate = null!; private Sample tapSample = null!; + private bool visible; + [BackgroundDependencyLoader] private void load(OsuConfigManager config, ScreenshotManager? screenshotManager, AudioManager audio) { @@ -52,46 +54,41 @@ namespace osu.Game.Graphics.Cursor [Resolved] private OsuGame? game { get; set; } - private readonly IBindable mouseInputSource = new BindableBool(); + private readonly IBindable lastInputWasMouse = new BindableBool(); private readonly IBindable isIdle = new BindableBool(); - private readonly Bindable internalState = new Bindable(); - protected override void LoadComplete() { base.LoadComplete(); - internalState.ValueChanged += onInternalStateChanged; - if (inputManager != null) { - mouseInputSource.BindTo(inputManager.IsMouseInputSource); - mouseInputSource.BindValueChanged(_ => updateInternalVisibility(), true); + lastInputWasMouse.BindTo(inputManager.LastInputWasMouseSource); + lastInputWasMouse.BindValueChanged(_ => updateState(), true); } if (game != null) { isIdle.BindTo(game.IsIdle); - isIdle.BindValueChanged(_ => updateInternalVisibility()); + isIdle.BindValueChanged(_ => updateState()); } } - private void updateInternalVisibility() - { - bool visible = mouseInputSource.Value; - internalState.Value = visible ? Visibility.Visible : Visibility.Hidden; - } + protected override void UpdateState(ValueChangedEvent state) => updateState(); - private void onInternalStateChanged(ValueChangedEvent internalState) + private void updateState() { - if (State.Value == Visibility.Visible) - base.UpdateState(internalState); - } + bool combinedVisibility = State.Value == Visibility.Visible && lastInputWasMouse.Value && !isIdle.Value; - protected override void UpdateState(ValueChangedEvent state) - { - if (internalState.Value == Visibility.Visible) - base.UpdateState(state); + if (visible == combinedVisibility) + return; + + visible = combinedVisibility; + + if (visible) + PopIn(); + else + PopOut(); } protected override void Update() diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs index 5e700e32de..82c6ca0edd 100644 --- a/osu.Game/Input/OsuUserInputManager.cs +++ b/osu.Game/Input/OsuUserInputManager.cs @@ -15,9 +15,9 @@ namespace osu.Game.Input /// /// Whether the last input applied to the game is sourced from mouse. /// - public IBindable IsMouseInputSource => isMouseInputSource; + public IBindable LastInputWasMouseSource => lastInputWasMouseSource; - private readonly Bindable isMouseInputSource = new Bindable(); + private readonly Bindable lastInputWasMouseSource = new Bindable(); internal OsuUserInputManager() { @@ -29,11 +29,11 @@ namespace osu.Game.Input { case ButtonStateChangeEvent: case MousePositionChangeEvent: - isMouseInputSource.Value = true; + lastInputWasMouseSource.Value = true; break; default: - isMouseInputSource.Value = false; + lastInputWasMouseSource.Value = false; break; } From dd372c4d19c87e28f53b3a3f92fa17d824fb9ddf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 13:51:02 +0900 Subject: [PATCH 050/321] Move "argon" skin judgement text in front of hitobjects --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs index e7dfec256d..6a9b3e3aba 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon ringExplosion?.PlayAnimation(); } - public Drawable? GetAboveHitObjectsProxiedContent() => null; + public Drawable? GetAboveHitObjectsProxiedContent() => JudgementText; private class RingExplosion : CompositeDrawable { diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs index b08b7b4e85..bb68c7298f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs @@ -75,6 +75,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { default: JudgementText + .FadeInFromZero(300, Easing.OutQuint) .ScaleTo(Vector2.One) .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); break; @@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon ringExplosion?.PlayAnimation(); } - public Drawable? GetAboveHitObjectsProxiedContent() => null; + public Drawable? GetAboveHitObjectsProxiedContent() => JudgementText.CreateProxy(); private class RingExplosion : CompositeDrawable { From 338115ff6a00d4eaf9ce90e061c71f9504307174 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 13 Oct 2022 15:05:15 +0900 Subject: [PATCH 051/321] Move check to LegacyDifficultyControlPoint --- .../Beatmaps/TaikoBeatmapConverter.cs | 9 +-------- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +++--- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 20 +++++++------------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 613c41a59a..524565a863 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -162,14 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps #pragma warning disable 618 if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint) #pragma warning restore 618 - { - double bpmMultiplier; - if (beatmap.BeatmapInfo.OnlineID == 1 || beatmap.BeatmapInfo.OnlineID == 3) - bpmMultiplier = legacyDifficultyPoint.BpmMultiplierMania; - else - bpmMultiplier = legacyDifficultyPoint.BpmMultiplier; - beatLength = timingPoint.BeatLength * bpmMultiplier; - } + beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; else beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 75500fbc4e..683bc19ce6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -427,8 +427,10 @@ namespace osu.Game.Beatmaps.Formats addControlPoint(time, controlPoint, true); } + int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; + #pragma warning disable 618 - addControlPoint(time, new LegacyDifficultyControlPoint(beatLength) + addControlPoint(time, new LegacyDifficultyControlPoint(onlineRulesetID, beatLength) #pragma warning restore 618 { SliderVelocity = speedMultiplier, @@ -440,8 +442,6 @@ namespace osu.Game.Beatmaps.Formats OmitFirstBarLine = omitFirstBarSignature, }; - int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; - // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. if (onlineRulesetID == 1 || onlineRulesetID == 3) effectPoint.ScrollSpeed = speedMultiplier; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 03819af149..ed7ca47cfd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -168,25 +168,21 @@ namespace osu.Game.Beatmaps.Formats /// public double BpmMultiplier { get; private set; } - /// - /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. - /// This is to be used for taiko and mania specific beatmaps. - /// DO NOT USE THIS UNLESS 100% SURE. - /// - public double BpmMultiplierMania { get; private set; } - /// /// Whether or not slider ticks should be generated at this control point. /// This exists for backwards compatibility with maps that abuse NaN slider velocity behavior on osu!stable (e.g. /b/2628991). /// public bool GenerateTicks { get; private set; } = true; - public LegacyDifficultyControlPoint(double beatLength) + public LegacyDifficultyControlPoint(int rulesetId, double beatLength) : this() { // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; - BpmMultiplierMania = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; + if (rulesetId == 1 || rulesetId == 3) + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; + else + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 1000) / 100.0 : 1; + GenerateTicks = !double.IsNaN(beatLength); } @@ -204,7 +200,6 @@ namespace osu.Game.Beatmaps.Formats base.CopyFrom(other); BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier; - BpmMultiplierMania = ((LegacyDifficultyControlPoint)other).BpmMultiplierMania; GenerateTicks = ((LegacyDifficultyControlPoint)other).GenerateTicks; } @@ -215,11 +210,10 @@ namespace osu.Game.Beatmaps.Formats public bool Equals(LegacyDifficultyControlPoint? other) => base.Equals(other) && BpmMultiplier == other.BpmMultiplier - && BpmMultiplierMania == other.BpmMultiplierMania && GenerateTicks == other.GenerateTicks; // ReSharper disable twice NonReadonlyMemberInGetHashCode - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier, BpmMultiplierMania, GenerateTicks); + public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier, GenerateTicks); } internal class LegacySampleControlPoint : SampleControlPoint, IEquatable From f6f5d33f53c0f40ce5382bd95677093157e50eca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 15:44:23 +0900 Subject: [PATCH 052/321] Add slight padding to playfield (roughly matches stable) --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b3aafb9730..bcb084fd4e 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -106,6 +106,7 @@ namespace osu.Game.Screens.Edit Name = "Main content", RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, + Padding = new MarginPadding(10), Child = spinner = new LoadingSpinner(true) { State = { Value = Visibility.Visible }, From a525b3f9f81d64da3c31f9fb29ac1e9bccf603dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 15:44:33 +0900 Subject: [PATCH 053/321] Reorder and rename toggle checkboxes to fit better --- .../Edit/Compose/Components/Timeline/TimelineArea.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index c2415ce978..58d378154a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -78,16 +78,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline LabelText = "Waveform", Current = { Value = true }, }, - controlPointsCheckbox = new OsuCheckbox - { - LabelText = "Control Points", - Current = { Value = true }, - }, ticksCheckbox = new OsuCheckbox { LabelText = "Ticks", Current = { Value = true }, - } + }, + controlPointsCheckbox = new OsuCheckbox + { + LabelText = "BPM", + Current = { Value = true }, + }, } } } From c3902728f65813f693ed9a5254e01b7eb313b8e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 15:47:54 +0900 Subject: [PATCH 054/321] Reorganise beatmap snap control to work better in compact mode --- .../Compose/Components/BeatDivisorControl.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 40403e08ad..19ea2162a3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -123,16 +123,6 @@ namespace osu.Game.Screens.Edit.Compose.Components } }, new Drawable[] - { - new TextFlowContainer(s => s.Font = s.Font.With(size: 14)) - { - Padding = new MarginPadding { Horizontal = 15 }, - Text = "beat snap", - RelativeSizeAxes = Axes.X, - TextAnchor = Anchor.TopCentre - }, - }, - new Drawable[] { new Container { @@ -173,6 +163,16 @@ namespace osu.Game.Screens.Edit.Compose.Components } } }, + new Drawable[] + { + new TextFlowContainer(s => s.Font = s.Font.With(size: 14)) + { + Padding = new MarginPadding { Horizontal = 15, Vertical = 8 }, + Text = "beat snap", + RelativeSizeAxes = Axes.X, + TextAnchor = Anchor.TopCentre, + }, + }, }, RowDimensions = new[] { From 0f4a2a605941a53772e6318450906b866310ff98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 16:04:38 +0900 Subject: [PATCH 055/321] Improve feel of settings toolbox group --- .../TestSceneSettingsToolboxGroup.cs | 8 +++ osu.Game/Overlays/SettingsToolboxGroup.cs | 69 ++++++++++--------- osu.Game/Screens/Play/PlayerLoader.cs | 3 + 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index 50e506f82b..9fb0905a4f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -5,12 +5,14 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osu.Game.Overlays.Settings; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -20,11 +22,17 @@ namespace osu.Game.Tests.Visual.UserInterface { private SettingsToolboxGroup group; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + [SetUp] public void SetUp() => Schedule(() => { Child = group = new SettingsToolboxGroup("example") { + Scale = new Vector2(3), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Children = new Drawable[] { new RoundedButton diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 548d75c9a9..2f47019272 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -1,8 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Extensions.EnumExtensions; @@ -23,25 +22,40 @@ namespace osu.Game.Overlays { public class SettingsToolboxGroup : Container, IExpandable { + private readonly string title; public const int CONTAINER_WIDTH = 270; private const float transition_duration = 250; - private const int border_thickness = 2; private const int header_height = 30; private const int corner_radius = 5; - private const float fade_duration = 800; - private const float inactive_alpha = 0.5f; - private readonly Cached headerTextVisibilityCache = new Cached(); - private readonly FillFlowContainer content; + protected override Container Content => content; + + private readonly FillFlowContainer content = new FillFlowContainer + { + Name = @"Content", + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeDuration = transition_duration, + AutoSizeEasing = Easing.OutQuint, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10, Top = 5, Bottom = 10 }, + Spacing = new Vector2(0, 15), + }; public BindableBool Expanded { get; } = new BindableBool(true); - private readonly OsuSpriteText headerText; + private OsuSpriteText headerText = null!; - private readonly Container headerContent; + private Container headerContent = null!; + + private Box background = null!; + + private IconButton expandButton = null!; /// /// Create a new instance. @@ -49,20 +63,24 @@ namespace osu.Game.Overlays /// The title to be displayed in the header of this group. public SettingsToolboxGroup(string title) { + this.title = title; + AutoSizeAxes = Axes.Y; Width = CONTAINER_WIDTH; Masking = true; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { CornerRadius = corner_radius; - BorderColour = Color4.Black; - BorderThickness = border_thickness; InternalChildren = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, + Colour = colourProvider.Background5, }, new FillFlowContainer { @@ -88,7 +106,7 @@ namespace osu.Game.Overlays Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 17), Padding = new MarginPadding { Left = 10, Right = 30 }, }, - new IconButton + expandButton = new IconButton { Origin = Anchor.Centre, Anchor = Anchor.CentreRight, @@ -99,19 +117,7 @@ namespace osu.Game.Overlays }, } }, - content = new FillFlowContainer - { - Name = @"Content", - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(15), - Spacing = new Vector2(0, 15), - } + content } }, }; @@ -175,9 +181,10 @@ namespace osu.Game.Overlays private void updateFadeState() { - this.FadeTo(IsHovered ? 1 : inactive_alpha, fade_duration, Easing.OutQuint); - } + const float fade_duration = 500; - protected override Container Content => content; + background.FadeTo(IsHovered ? 1 : 0, fade_duration, Easing.OutQuint); + expandButton.FadeTo(IsHovered ? 1 : 0, fade_duration, Easing.OutQuint); + } } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e32d3d90be..2d096f1c38 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -71,6 +71,9 @@ namespace osu.Game.Screens.Play private AudioFilter lowPassFilter = null!; private AudioFilter highPassFilter = null!; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + protected bool BackgroundBrightnessReduction { set From 4d99c7002be11fa31e5d22c91227cb7cceb80084 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 16:33:01 +0900 Subject: [PATCH 056/321] Add background behind editor toolbox groups --- .../Edit/DistancedHitObjectComposer.cs | 31 ++++++++++---- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 40 +++++++++++++------ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 4726211666..5f1f7a1fbf 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -8,6 +8,8 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; @@ -52,20 +54,33 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { - AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) + AddInternal(new Container { - Padding = new MarginPadding(10), - Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Child = new EditorToolboxGroup("snapping") + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] { - Child = distanceSpacingSlider = new ExpandableSlider> + new Box { - Current = { BindTarget = DistanceSpacingMultiplier }, - KeyboardStep = adjust_step, + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.Both, + }, + RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) + { + Padding = new MarginPadding(10), + Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, + Child = new EditorToolboxGroup("snapping") + { + Child = distanceSpacingSlider = new ExpandableSlider> + { + Current = { BindTarget = DistanceSpacingMultiplier }, + KeyboardStep = adjust_step, + } + } } } }); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 37c66037eb..63f0c64c80 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -12,10 +12,12 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -80,7 +82,7 @@ namespace osu.Game.Rulesets.Edit dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { Config = Dependencies.Get().GetConfigFor(Ruleset); @@ -116,25 +118,37 @@ namespace osu.Game.Rulesets.Edit .WithChild(BlueprintContainer = CreateBlueprintContainer()) } }, - new ExpandingToolboxContainer(90, 200) + new Container { - Padding = new MarginPadding(10), + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, Children = new Drawable[] { - new EditorToolboxGroup("toolbox (1-9)") + new Box { - Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X } + Colour = colourProvider.Background5, + RelativeSizeAxes = Axes.Both, }, - new EditorToolboxGroup("toggles (Q~P)") + new ExpandingToolboxContainer(60, 200) { - Child = togglesCollection = new FillFlowContainer + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - }, - } + new EditorToolboxGroup("toolbox (1-9)") + { + Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X } + }, + new EditorToolboxGroup("toggles (Q~P)") + { + Child = togglesCollection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + }, + } + } + }, } }, }; From 15f9697c9f0d69ff3a09587761b3b72a9e15f7a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 16:33:09 +0900 Subject: [PATCH 057/321] Roughly update design of editor buttons --- .../RadioButtons/EditorRadioButton.cs | 26 ++++++---------- .../TernaryButtons/DrawableTernaryButton.cs | 30 +++++++------------ 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index 82e94dc862..071bb9fdcb 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -8,13 +8,12 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -30,9 +29,9 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons public readonly RadioButton Button; private Color4 defaultBackgroundColour; - private Color4 defaultBubbleColour; + private Color4 defaultIconColour; private Color4 selectedBackgroundColour; - private Color4 selectedBubbleColour; + private Color4 selectedIconColour; private Drawable icon; @@ -50,20 +49,13 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - defaultBackgroundColour = colours.Gray3; - defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); - selectedBackgroundColour = colours.BlueDark; - selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); + defaultBackgroundColour = colourProvider.Background3; + selectedBackgroundColour = colourProvider.Background1; - Content.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 2, - Offset = new Vector2(0, 1), - Colour = Color4.Black.Opacity(0.5f) - }; + defaultIconColour = defaultBackgroundColour.Darken(0.5f); + selectedIconColour = selectedBackgroundColour.Lighten(0.5f); Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b => { @@ -98,7 +90,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons return; BackgroundColour = Button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour; - icon.Colour = Button.Selected.Value ? selectedBubbleColour : defaultBubbleColour; + icon.Colour = Button.Selected.Value ? selectedIconColour : defaultIconColour; } protected override SpriteText CreateText() => new OsuSpriteText diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs index 55302833c1..1fb5c0285d 100644 --- a/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs +++ b/osu.Game/Screens/Edit/Components/TernaryButtons/DrawableTernaryButton.cs @@ -6,12 +6,11 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -20,9 +19,9 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons internal class DrawableTernaryButton : OsuButton { private Color4 defaultBackgroundColour; - private Color4 defaultBubbleColour; + private Color4 defaultIconColour; private Color4 selectedBackgroundColour; - private Color4 selectedBubbleColour; + private Color4 selectedIconColour; private Drawable icon; @@ -38,20 +37,13 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - defaultBackgroundColour = colours.Gray3; - defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); - selectedBackgroundColour = colours.BlueDark; - selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); + defaultBackgroundColour = colourProvider.Background3; + selectedBackgroundColour = colourProvider.Background1; - Content.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 2, - Offset = new Vector2(0, 1), - Colour = Color4.Black.Opacity(0.5f) - }; + defaultIconColour = defaultBackgroundColour.Darken(0.5f); + selectedIconColour = selectedBackgroundColour.Lighten(0.5f); Add(icon = (Button.CreateIcon?.Invoke() ?? new Circle()).With(b => { @@ -85,17 +77,17 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons switch (Button.Bindable.Value) { case TernaryState.Indeterminate: - icon.Colour = selectedBubbleColour.Darken(0.5f); + icon.Colour = selectedIconColour.Darken(0.5f); BackgroundColour = selectedBackgroundColour.Darken(0.5f); break; case TernaryState.False: - icon.Colour = defaultBubbleColour; + icon.Colour = defaultIconColour; BackgroundColour = defaultBackgroundColour; break; case TernaryState.True: - icon.Colour = selectedBubbleColour; + icon.Colour = selectedIconColour; BackgroundColour = selectedBackgroundColour; break; } From 6608ada925cc9f12987723b419e10132ee1bab37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 16:57:37 +0900 Subject: [PATCH 058/321] Fix test failures due to missing colour provider dependency --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 4 ++++ .../Visual/Editing/TestSceneEditorComposeRadioButtons.cs | 5 +++++ osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 4 ---- osu.Game/Overlays/SettingsToolboxGroup.cs | 6 +++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index c102678e00..dc74d38cdc 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Overlays; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Edit; @@ -33,6 +34,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Cached(typeof(IBeatSnapProvider))] private readonly EditorBeatmap editorBeatmap; + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + [Cached] private readonly EditorClock editorClock; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs index 56435c69a4..6a69347651 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs @@ -4,8 +4,10 @@ #nullable disable using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Components.RadioButtons; namespace osu.Game.Tests.Visual.Editing @@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorComposeRadioButtons : OsuTestScene { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + public TestSceneEditorComposeRadioButtons() { EditorRadioButtonCollection collection; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index adb495f3d3..58b5b41702 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -148,10 +148,6 @@ namespace osu.Game.Tests.Visual.Editing }); AddAssert("no circles placed", () => editorBeatmap.HitObjects.Count == 0); - - AddStep("place circle", () => InputManager.Click(MouseButton.Left)); - - AddAssert("circle placed", () => editorBeatmap.HitObjects.Count == 1); } [Test] diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 2f47019272..008479b8e0 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -70,8 +70,8 @@ namespace osu.Game.Overlays Masking = true; } - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + [BackgroundDependencyLoader(true)] + private void load(OverlayColourProvider? colourProvider) { CornerRadius = corner_radius; @@ -80,7 +80,7 @@ namespace osu.Game.Overlays background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5, + Colour = colourProvider?.Background5 ?? Color4.Black, }, new FillFlowContainer { From a93c350ca85944d38b09744801a7ac8f6b361411 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 17:11:56 +0900 Subject: [PATCH 059/321] Start settings panels without background visible --- osu.Game/Overlays/SettingsToolboxGroup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 008479b8e0..37c8ec749d 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -80,6 +80,7 @@ namespace osu.Game.Overlays background = new Box { RelativeSizeAxes = Axes.Both, + Alpha = 0, Colour = colourProvider?.Background5 ?? Color4.Black, }, new FillFlowContainer From b33935e39225104f740fc4c9611f4d5cd13f44d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 17:12:28 +0900 Subject: [PATCH 060/321] Fix player loading screen settings scaling in This always looked weird, and from the looks of the structure was probably unintentional from the start (it is in the logo tracking container for whatever reason). --- osu.Game/Screens/Play/PlayerLoader.cs | 51 +++++++++++++++------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2d096f1c38..0065520268 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -64,6 +64,8 @@ namespace osu.Game.Screens.Play protected Task? DisposalTask { get; private set; } + private OsuScrollContainer settingsScroll = null!; + private bool backgroundBrightnessReduction; private readonly BindableDouble volumeAdjustment = new BindableDouble(1); @@ -168,30 +170,30 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - new OsuScrollContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2, - Padding = new MarginPadding { Vertical = padding }, - Masking = false, - Child = PlayerSettings = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Padding = new MarginPadding { Horizontal = padding }, - Children = new PlayerSettingsGroup[] - { - VisualSettings = new VisualSettings(), - AudioSettings = new AudioSettings(), - new InputSettings() - } - }, - }, - idleTracker = new IdleTracker(750), }), + settingsScroll = new OsuScrollContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2, + Padding = new MarginPadding { Vertical = padding }, + Masking = false, + Child = PlayerSettings = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Padding = new MarginPadding { Horizontal = padding }, + Children = new PlayerSettingsGroup[] + { + VisualSettings = new VisualSettings(), + AudioSettings = new AudioSettings(), + new InputSettings() + } + }, + }, + idleTracker = new IdleTracker(750), lowPassFilter = new AudioFilter(audio.TrackMixer), highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass) }; @@ -392,6 +394,8 @@ namespace osu.Game.Screens.Play { MetadataInfo.Loading = true; + settingsScroll.FadeInFromZero(400); + content.FadeInFromZero(400); content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer); lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint); @@ -407,6 +411,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, CONTENT_OUT_DURATION * 2, Easing.OutQuint); content.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint); + settingsScroll.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint); lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, CONTENT_OUT_DURATION); highPassFilter.CutoffTo(0, CONTENT_OUT_DURATION); } From 3645269108a388196e3cb254b4acae5e54c836c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 Oct 2022 17:29:07 +0900 Subject: [PATCH 061/321] Improve logo transition and reduce delay of exiting player loader in optimal conditions --- osu.Game/Screens/Play/PlayerLoader.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 0065520268..b553818a2d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -318,6 +318,16 @@ namespace osu.Game.Screens.Play content.StopTracking(); } + protected override void LogoSuspending(OsuLogo logo) + { + base.LogoSuspending(logo); + content.StopTracking(); + + logo + .FadeOut(CONTENT_OUT_DURATION / 2, Easing.OutQuint) + .ScaleTo(logo.Scale * 0.8f, CONTENT_OUT_DURATION * 2, Easing.OutQuint); + } + #endregion protected override void Update() @@ -440,7 +450,7 @@ namespace osu.Game.Screens.Play ContentOut(); - TransformSequence pushSequence = this.Delay(CONTENT_OUT_DURATION); + TransformSequence pushSequence = this.Delay(0); // only show if the warning was created (i.e. the beatmap needs it) // and this is not a restart of the map (the warning expires after first load). @@ -449,6 +459,7 @@ namespace osu.Game.Screens.Play const double epilepsy_display_length = 3000; pushSequence + .Delay(CONTENT_OUT_DURATION) .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) .Delay(epilepsy_display_length) From df66a0c2e9ce41e5b180cf33f9e82c0864838fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 13 Oct 2022 21:03:52 +0200 Subject: [PATCH 062/321] Fix flaky update beatmap set test It is generally not possible to click a button that's not yet there, and it turns out that when the test in question is ran headless, it may not necessarily be there immediately. --- .../Visual/SongSelect/TestSceneUpdateBeatmapSetButton.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUpdateBeatmapSetButton.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUpdateBeatmapSetButton.cs index f76f050546..96cfbe4dd1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUpdateBeatmapSetButton.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUpdateBeatmapSetButton.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -150,6 +151,7 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestUpdateLocalBeatmap() { DialogOverlay dialogOverlay = null!; + UpdateBeatmapSetButton? updateButton = null; AddStep("create carousel with dialog overlay", () => { @@ -176,7 +178,8 @@ namespace osu.Game.Tests.Visual.SongSelect carousel.UpdateBeatmapSet(testBeatmapSetInfo); }); - AddStep("click button", () => getUpdateButton()?.TriggerClick()); + AddUntilStep("wait for update button", () => (updateButton = getUpdateButton()) != null); + AddStep("click button", () => updateButton.AsNonNull().TriggerClick()); AddAssert("dialog displayed", () => dialogOverlay.CurrentDialog is UpdateLocalConfirmationDialog); AddStep("click confirmation", () => From e97dbf3dbce8538793ab8b477c51b5437ba75eed Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 22:12:32 +0300 Subject: [PATCH 063/321] Move copy toast to its own file --- osu.Game/Graphics/UserInterface/CopyUrlToast.cs | 16 ++++++++++++++++ .../Graphics/UserInterface/ExternalLinkButton.cs | 12 +----------- 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/CopyUrlToast.cs diff --git a/osu.Game/Graphics/UserInterface/CopyUrlToast.cs b/osu.Game/Graphics/UserInterface/CopyUrlToast.cs new file mode 100644 index 0000000000..66aaf1a212 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/CopyUrlToast.cs @@ -0,0 +1,16 @@ +// 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.Localisation; +using osu.Game.Overlays.OSD; + +namespace osu.Game.Graphics.UserInterface +{ + public class CopyUrlToast : Toast + { + public CopyUrlToast() + : base(UserInterfaceStrings.GeneralHeader, ToastStrings.UrlCopied, "") + { + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 7f86a060ad..288539f421 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -10,10 +10,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Game.Localisation; using osu.Framework.Platform; using osu.Game.Overlays; -using osu.Game.Overlays.OSD; using osuTK; using osuTK.Graphics; @@ -94,15 +92,7 @@ namespace osu.Game.Graphics.UserInterface private void copyUrl() { host.GetClipboard()?.SetText(Link); - onScreenDisplay?.Display(new CopyUrlToast(ToastStrings.UrlCopied)); - } - - private class CopyUrlToast : Toast - { - public CopyUrlToast(LocalisableString value) - : base(UserInterfaceStrings.GeneralHeader, value, "") - { - } + onScreenDisplay?.Display(new CopyUrlToast()); } } } From a0cca391f6365cd892862701a1aa58d316cd2edc Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 22:18:26 +0300 Subject: [PATCH 064/321] Add copy link button to comments --- osu.Game/Overlays/Comments/DrawableComment.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 193d15064a..1f18540044 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -20,6 +20,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions.IEnumerableExtensions; using System.Collections.Specialized; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -71,6 +72,12 @@ namespace osu.Game.Overlays.Comments [Resolved] private IAPIProvider api { get; set; } = null!; + [Resolved] + private GameHost host { get; set; } = null!; + + [Resolved(canBeNull: true)] + private OnScreenDisplay? onScreenDisplay { get; set; } + public DrawableComment(Comment comment) { Comment = comment; @@ -323,6 +330,8 @@ namespace osu.Game.Overlays.Comments if (WasDeleted) makeDeleted(); + actionsContainer.AddLink("Copy URL", copyUrl); + if (Comment.UserId.HasValue && Comment.UserId.Value == api.LocalUser.Value.Id) { actionsContainer.AddLink("Delete", deleteComment); @@ -403,6 +412,12 @@ namespace osu.Game.Overlays.Comments api.Queue(request); } + private void copyUrl() + { + host.GetClipboard()?.SetText($@"https://osu.ppy.sh/comments/{Comment.Id}"); + onScreenDisplay?.Display(new CopyUrlToast()); + } + protected override void LoadComplete() { ShowDeleted.BindValueChanged(show => From 34ff072a9b2eb51837bbadedcddae25fd537707e Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 22:47:48 +0300 Subject: [PATCH 065/321] Change text --- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 1f18540044..9c82c9aa98 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -330,7 +330,7 @@ namespace osu.Game.Overlays.Comments if (WasDeleted) makeDeleted(); - actionsContainer.AddLink("Copy URL", copyUrl); + actionsContainer.AddLink("Copy link", copyUrl); if (Comment.UserId.HasValue && Comment.UserId.Value == api.LocalUser.Value.Id) { From 194bb02c28279d32c1673749824a1adfacdb4746 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 22:47:53 +0300 Subject: [PATCH 066/321] Add test --- .../Visual/Online/TestSceneCommentActions.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs index bf01d3b0ac..59a25880c4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -35,6 +36,9 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + [Resolved] + private GameHost host { get; set; } = null!; + private CommentsContainer commentsContainer = null!; [BackgroundDependencyLoader] @@ -204,6 +208,22 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestCopyLink() + { + addTestComments(); + AddUntilStep("Comments loaded", () => this.ChildrenOfType().Count() == 2); + AddStep("Click copy link", () => + { + var comment = this.ChildrenOfType().First(); + var links = comment.ChildrenOfType().Single(x => x.Name == @"Actions buttons"); + var btn = links.ChildrenOfType().Single(x => x.Text.ToString().StartsWith("Copy")); + InputManager.MoveMouseTo(btn); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("Link copied", () => host.GetClipboard()?.GetText() == "https://osu.ppy.sh/comments/1"); + } + private void addTestComments() { AddStep("set up response", () => From 310588ad94c8ef58d220d0885762435678dce116 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Thu, 13 Oct 2022 13:01:05 -0700 Subject: [PATCH 067/321] Renew smoke rng every draw --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 6c998e244c..8316ece2b3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -216,8 +216,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeEndTime = Source.smokeEndTime; CurrentTime = Source.Clock.CurrentTime; - rotationRNG = new Random(Source.rotationSeed); - initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; @@ -233,6 +231,8 @@ namespace osu.Game.Rulesets.Osu.Skinning if (points.Count == 0) return; + rotationRNG = new Random(Source.rotationSeed); + quadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); texture ??= renderer.WhitePixel; RectangleF textureRect = texture.GetTextureRect(); From 295e6f5a680a20418857e94c24eb0c04800fec66 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 23:57:25 +0300 Subject: [PATCH 068/321] Move toast to it's namespace --- osu.Game/Graphics/UserInterface/ExternalLinkButton.cs | 1 + osu.Game/Overlays/Comments/DrawableComment.cs | 1 + .../{Graphics/UserInterface => Overlays/OSD}/CopyUrlToast.cs | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) rename osu.Game/{Graphics/UserInterface => Overlays/OSD}/CopyUrlToast.cs (83%) diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index 288539f421..e51dbeed14 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -12,6 +12,7 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Overlays; +using osu.Game.Overlays.OSD; using osuTK; using osuTK.Graphics; diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 9c82c9aa98..7a8625906c 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -26,6 +26,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Comments.Buttons; using osu.Game.Overlays.Dialog; +using osu.Game.Overlays.OSD; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Comments diff --git a/osu.Game/Graphics/UserInterface/CopyUrlToast.cs b/osu.Game/Overlays/OSD/CopyUrlToast.cs similarity index 83% rename from osu.Game/Graphics/UserInterface/CopyUrlToast.cs rename to osu.Game/Overlays/OSD/CopyUrlToast.cs index 66aaf1a212..ea835a1c5e 100644 --- a/osu.Game/Graphics/UserInterface/CopyUrlToast.cs +++ b/osu.Game/Overlays/OSD/CopyUrlToast.cs @@ -2,9 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Localisation; -using osu.Game.Overlays.OSD; -namespace osu.Game.Graphics.UserInterface +namespace osu.Game.Overlays.OSD { public class CopyUrlToast : Toast { From a375c3389b0646c26f418683d2bdc500221623fd Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Thu, 13 Oct 2022 23:59:36 +0300 Subject: [PATCH 069/321] Delete the test --- .../Visual/Online/TestSceneCommentActions.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs index 59a25880c4..bf01d3b0ac 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs @@ -10,7 +10,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -36,9 +35,6 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - [Resolved] - private GameHost host { get; set; } = null!; - private CommentsContainer commentsContainer = null!; [BackgroundDependencyLoader] @@ -208,22 +204,6 @@ namespace osu.Game.Tests.Visual.Online }); } - [Test] - public void TestCopyLink() - { - addTestComments(); - AddUntilStep("Comments loaded", () => this.ChildrenOfType().Count() == 2); - AddStep("Click copy link", () => - { - var comment = this.ChildrenOfType().First(); - var links = comment.ChildrenOfType().Single(x => x.Name == @"Actions buttons"); - var btn = links.ChildrenOfType().Single(x => x.Text.ToString().StartsWith("Copy")); - InputManager.MoveMouseTo(btn); - InputManager.Click(MouseButton.Left); - }); - AddUntilStep("Link copied", () => host.GetClipboard()?.GetText() == "https://osu.ppy.sh/comments/1"); - } - private void addTestComments() { AddStep("set up response", () => From 8a47628fe1c98a46c68b0037e41efa1a34bb9452 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Fri, 14 Oct 2022 00:04:13 +0300 Subject: [PATCH 070/321] Use spacer drawable instead of spacing on links container --- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 7a8625906c..59f6b74722 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -211,7 +211,6 @@ namespace osu.Game.Overlays.Comments { Name = @"Actions buttons", AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10, 0) }, actionsLoading = new LoadingSpinner { @@ -332,6 +331,7 @@ namespace osu.Game.Overlays.Comments makeDeleted(); actionsContainer.AddLink("Copy link", copyUrl); + actionsContainer.AddArbitraryDrawable(new Container { Width = 10 }); if (Comment.UserId.HasValue && Comment.UserId.Value == api.LocalUser.Value.Id) { From 029f896db86b07371849a1440c295427878ac9fd Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Thu, 13 Oct 2022 16:18:47 -0700 Subject: [PATCH 071/321] Use `StatelessRNG` for smoke rotation --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 8316ece2b3..7af1b5d86a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -185,6 +186,8 @@ namespace osu.Game.Rulesets.Osu.Skinning private float radius; private Vector2 drawSize; private Texture? texture; + private int rotationSeed; + private int rotationIndex; // anim calculation vars (color, scale, direction) private double initialFadeOutDurationTrunc; @@ -194,8 +197,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private double reFadeInTime; private double finalFadeOutTime; - private Random rotationRNG = new Random(); - public SmokeDrawNode(ITexturedShaderDrawable source) : base(source) { @@ -216,6 +217,8 @@ namespace osu.Game.Rulesets.Osu.Skinning SmokeEndTime = Source.smokeEndTime; CurrentTime = Source.Clock.CurrentTime; + rotationSeed = Source.rotationSeed; + initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; @@ -231,7 +234,7 @@ namespace osu.Game.Rulesets.Osu.Skinning if (points.Count == 0) return; - rotationRNG = new Random(Source.rotationSeed); + rotationIndex = rotationSeed; quadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); texture ??= renderer.WhitePixel; @@ -311,7 +314,11 @@ namespace osu.Game.Rulesets.Osu.Skinning return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } - private float nextRotation() => max_rotation * ((float)rotationRNG.NextDouble() * 2 - 1); + private float nextRotation() + { + rotationIndex++; + return max_rotation * (StatelessRNG.NextSingle(rotationIndex) * 2 - 1); + } private void drawPointQuad(SmokePoint point, RectangleF textureRect) { From 516e99df7264b59fe4097bfffb358bf3c88e1e44 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 Oct 2022 02:38:43 +0300 Subject: [PATCH 072/321] Add test coverage for flashlight mod --- .../Mods/TestSceneCatchModFlashlight.cs | 23 +++++++++++++++++ .../Mods/TestSceneManiaModFlashlight.cs | 23 +++++++++++++++++ .../Mods/TestSceneOsuModFlashlight.cs | 25 +++++++++++++++++++ .../TestSceneOsuFlashlight.cs | 21 ---------------- .../Mods/TestSceneTaikoModFlashlight.cs | 20 +++++++++++++++ 5 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModFlashlight.cs create mode 100644 osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFlashlight.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs delete mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModFlashlight.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModFlashlight.cs new file mode 100644 index 0000000000..538fc7fac6 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModFlashlight.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Catch.Tests.Mods +{ + public class TestSceneCatchModFlashlight : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + + [TestCase(1f)] + [TestCase(0.5f)] + [TestCase(1.25f)] + [TestCase(1.5f)] + public void TestSizeMultiplier(float sizeMultiplier) => CreateModTest(new ModTestData { Mod = new CatchModFlashlight { SizeMultiplier = { Value = sizeMultiplier } }, PassCondition = () => true }); + + [Test] + public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new CatchModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFlashlight.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFlashlight.cs new file mode 100644 index 0000000000..0e222fea89 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFlashlight.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Mods +{ + public class TestSceneManiaModFlashlight : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + [TestCase(1f)] + [TestCase(0.5f)] + [TestCase(1.5f)] + [TestCase(3f)] + public void TestSizeMultiplier(float sizeMultiplier) => CreateModTest(new ModTestData { Mod = new ManiaModFlashlight { SizeMultiplier = { Value = sizeMultiplier } }, PassCondition = () => true }); + + [Test] + public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new ManiaModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs new file mode 100644 index 0000000000..704a548c61 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModFlashlight : OsuModTestScene + { + [TestCase(600)] + [TestCase(120)] + [TestCase(1200)] + public void TestFollowDelay(double followDelay) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { FollowDelay = { Value = followDelay } }, PassCondition = () => true }); + + [TestCase(1f)] + [TestCase(0.5f)] + [TestCase(1.5f)] + [TestCase(2f)] + public void TestSizeMultiplier(float sizeMultiplier) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { SizeMultiplier = { Value = sizeMultiplier } }, PassCondition = () => true }); + + [Test] + public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs deleted file mode 100644 index e0d1646cb0..0000000000 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Osu.Tests -{ - public class TestSceneOsuFlashlight : TestSceneOsuPlayer - { - protected override TestPlayer CreatePlayer(Ruleset ruleset) - { - SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; - - return base.CreatePlayer(ruleset); - } - } -} diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs new file mode 100644 index 0000000000..417b59f5d2 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Taiko.Mods; + +namespace osu.Game.Rulesets.Taiko.Tests.Mods +{ + public class TestSceneTaikoModFlashlight : TaikoModTestScene + { + [TestCase(1f)] + [TestCase(0.5f)] + [TestCase(1.25f)] + [TestCase(1.5f)] + public void TestSizeMultiplier(float sizeMultiplier) => CreateModTest(new ModTestData { Mod = new TaikoModFlashlight { SizeMultiplier = { Value = sizeMultiplier } }, PassCondition = () => true }); + + [Test] + public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new TaikoModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + } +} From 525f98c1581540116a8633b76076ec67362bd396 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 14 Oct 2022 18:01:54 +0900 Subject: [PATCH 073/321] Fix max combo missing from playlists results screen --- osu.Game/Online/Rooms/MultiplayerScore.cs | 4 +++- .../Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs index 6f597e5b10..d5e0c7a970 100644 --- a/osu.Game/Online/Rooms/MultiplayerScore.cs +++ b/osu.Game/Online/Rooms/MultiplayerScore.cs @@ -65,7 +65,7 @@ namespace osu.Game.Online.Rooms [CanBeNull] public MultiplayerScoresAround ScoresAround { get; set; } - public ScoreInfo CreateScoreInfo(RulesetStore rulesets, PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap) + public ScoreInfo CreateScoreInfo(ScoreManager scoreManager, RulesetStore rulesets, PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap) { var ruleset = rulesets.GetRuleset(playlistItem.RulesetID); if (ruleset == null) @@ -90,6 +90,8 @@ namespace osu.Game.Online.Rooms Position = Position, }; + scoreManager.PopulateMaximumStatistics(scoreInfo); + return scoreInfo; } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs index 41633c34ce..27193d3cb6 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs @@ -182,7 +182,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// An optional pivot around which the scores were retrieved. private void performSuccessCallback([NotNull] Action> callback, [NotNull] List scores, [CanBeNull] MultiplayerScores pivot = null) => Schedule(() => { - var scoreInfos = scoreManager.OrderByTotalScore(scores.Select(s => s.CreateScoreInfo(rulesets, playlistItem, Beatmap.Value.BeatmapInfo))).ToArray(); + var scoreInfos = scoreManager.OrderByTotalScore(scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, Beatmap.Value.BeatmapInfo))).ToArray(); // Select a score if we don't already have one selected. // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). From 2404bb42b9d2fc6980d5cffa305b63a4be733944 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Oct 2022 19:33:49 +0900 Subject: [PATCH 074/321] Use 10% alpha rather than 0% when toolbox is not hovered --- osu.Game/Overlays/SettingsToolboxGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 37c8ec749d..27f727c9e3 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -80,7 +80,7 @@ namespace osu.Game.Overlays background = new Box { RelativeSizeAxes = Axes.Both, - Alpha = 0, + Alpha = 0.1f, Colour = colourProvider?.Background5 ?? Color4.Black, }, new FillFlowContainer @@ -184,7 +184,7 @@ namespace osu.Game.Overlays { const float fade_duration = 500; - background.FadeTo(IsHovered ? 1 : 0, fade_duration, Easing.OutQuint); + background.FadeTo(IsHovered ? 1 : 0.1f, fade_duration, Easing.OutQuint); expandButton.FadeTo(IsHovered ? 1 : 0, fade_duration, Easing.OutQuint); } } From 32c3e35762eeb42f8088717f99097594278504b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Oct 2022 19:38:27 +0900 Subject: [PATCH 075/321] Change background colour to a lighter tint to avoid clash with slider bars --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 +- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 27f727c9e3..04c9a626b5 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -81,7 +81,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.Both, Alpha = 0.1f, - Colour = colourProvider?.Background5 ?? Color4.Black, + Colour = colourProvider?.Background4 ?? Color4.Black, }, new FillFlowContainer { diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 5f1f7a1fbf..667a3cb409 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -71,7 +71,6 @@ namespace osu.Game.Rulesets.Edit }, RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250) { - Padding = new MarginPadding(10), Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Child = new EditorToolboxGroup("snapping") { From dcb6357964abbbd3643c83f6cd9a5c9556573d24 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 14 Oct 2022 20:23:55 +0900 Subject: [PATCH 076/321] Add ability to remove the current item in multiplayer --- .../Multiplayer/Match/Playlist/MultiplayerQueueList.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs index 39740e650f..ba6b482729 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs @@ -78,9 +78,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist return; bool isItemOwner = Item.OwnerID == api.LocalUser.Value.OnlineID || multiplayerClient.IsHost; + bool isValidItem = isItemOwner && !Item.Expired; - AllowDeletion = isItemOwner && !Item.Expired && Item.ID != multiplayerClient.Room.Settings.PlaylistItemId; - AllowEditing = isItemOwner && !Item.Expired; + AllowDeletion = isValidItem + && (Item.ID != multiplayerClient.Room.Settings.PlaylistItemId // This is an optimisation for the following check. + || multiplayerClient.Room.Playlist.Count(i => !i.Expired) > 1); + + AllowEditing = isValidItem; } protected override void Dispose(bool isDisposing) From b1b97505ac6704400b8817448d9a47b167956af8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 Oct 2022 22:14:23 +0300 Subject: [PATCH 077/321] Fix smoke displayed in "No scope" mod --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs | 7 ++++++- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 7 ++++++- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs index 44404ca245..0fb2a68cfa 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods @@ -145,6 +146,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private bool isBreak() => Player.IsBreakTime.Value; - private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.Alpha, alpha, 0.1f); + private OsuPlayfield playfield => (OsuPlayfield)Player.DrawableRuleset.Playfield; + + private bool cursorAlphaAlmostEquals(float alpha) => + Precision.AlmostEquals(playfield.Cursor.Alpha, alpha, 0.1f) && + Precision.AlmostEquals(playfield.Smoke.Alpha, alpha, 0.1f); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 2f84c30581..f5db8951d6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -9,6 +9,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osu.Game.Utils; @@ -35,7 +36,11 @@ namespace osu.Game.Rulesets.Osu.Mods { bool shouldAlwaysShowCursor = IsBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); float targetAlpha = shouldAlwaysShowCursor ? 1 : ComboBasedAlpha; - playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / TRANSITION_DURATION, 0, 1)); + float currentAlpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / TRANSITION_DURATION, 0, 1)); + + var osuPlayfield = (OsuPlayfield)playfield; + osuPlayfield.Cursor.Alpha = currentAlpha; + osuPlayfield.Smoke.Alpha = currentAlpha; } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 2e67e91460..e9a6c84c0b 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Osu.UI private readonly ProxyContainer spinnerProxies; private readonly JudgementContainer judgementLayer; + public SmokeContainer Smoke { get; } public FollowPointRenderer FollowPoints { get; } public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); @@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.UI InternalChildren = new Drawable[] { playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both }, - new SmokeContainer { RelativeSizeAxes = Axes.Both }, + Smoke = new SmokeContainer { RelativeSizeAxes = Axes.Both }, spinnerProxies = new ProxyContainer { RelativeSizeAxes = Axes.Both }, FollowPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both }, judgementLayer = new JudgementContainer { RelativeSizeAxes = Axes.Both }, From 46bcabea6c5714cb8a8980aa33cbff2ddc746233 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Fri, 14 Oct 2022 12:42:04 -0700 Subject: [PATCH 078/321] Separate smoke rotation seed and index --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 7af1b5d86a..aba4d0ff63 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Osu.Skinning if (points.Count == 0) return; - rotationIndex = rotationSeed; + rotationIndex = 0; quadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); texture ??= renderer.WhitePixel; @@ -314,11 +314,7 @@ namespace osu.Game.Rulesets.Osu.Skinning return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } - private float nextRotation() - { - rotationIndex++; - return max_rotation * (StatelessRNG.NextSingle(rotationIndex) * 2 - 1); - } + private float nextRotation() => max_rotation * (StatelessRNG.NextSingle(rotationSeed, rotationIndex++) * 2 - 1); private void drawPointQuad(SmokePoint point, RectangleF textureRect) { From 06e6713237ac8284e556411b88a2e2af49621ca5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 Oct 2022 00:18:24 +0300 Subject: [PATCH 079/321] Move mouse input detection inside `MenuCursorContainer` to allow testing --- .../Graphics/Cursor/MenuCursorContainer.cs | 44 +++++++++++++++---- osu.Game/Input/OsuUserInputManager.cs | 26 ----------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 94bdef9842..33b0c308cc 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Configuration; -using osu.Game.Input; using osuTK; namespace osu.Game.Graphics.Cursor @@ -35,6 +34,8 @@ namespace osu.Game.Graphics.Cursor private Bindable cursorRotate = null!; private Sample tapSample = null!; + private MouseInputDetector mouseInputDetector = null!; + private bool visible; [BackgroundDependencyLoader] @@ -46,10 +47,9 @@ namespace osu.Game.Graphics.Cursor screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility); tapSample = audio.Samples.Get(@"UI/cursor-tap"); - } - [Resolved] - private OsuUserInputManager? inputManager { get; set; } + Add(mouseInputDetector = new MouseInputDetector()); + } [Resolved] private OsuGame? game { get; set; } @@ -61,11 +61,8 @@ namespace osu.Game.Graphics.Cursor { base.LoadComplete(); - if (inputManager != null) - { - lastInputWasMouse.BindTo(inputManager.LastInputWasMouseSource); - lastInputWasMouse.BindValueChanged(_ => updateState(), true); - } + lastInputWasMouse.BindTo(mouseInputDetector.LastInputWasMouseSource); + lastInputWasMouse.BindValueChanged(_ => updateState(), true); if (game != null) { @@ -247,6 +244,35 @@ namespace osu.Game.Graphics.Cursor } } + private class MouseInputDetector : Component + { + /// + /// Whether the last input applied to the game is sourced from mouse. + /// + public IBindable LastInputWasMouseSource => lastInputWasMouseSource; + + private readonly Bindable lastInputWasMouseSource = new Bindable(); + + public MouseInputDetector() + { + RelativeSizeAxes = Axes.Both; + } + + protected override bool Handle(UIEvent e) + { + switch (e) + { + case MouseEvent: + lastInputWasMouseSource.Value = true; + return false; + + default: + lastInputWasMouseSource.Value = false; + return false; + } + } + } + private enum DragRotationState { NotDragging, diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs index 82c6ca0edd..7a9002a004 100644 --- a/osu.Game/Input/OsuUserInputManager.cs +++ b/osu.Game/Input/OsuUserInputManager.cs @@ -3,43 +3,17 @@ #nullable disable -using osu.Framework.Bindables; using osu.Framework.Input; -using osu.Framework.Input.StateChanges.Events; using osuTK.Input; namespace osu.Game.Input { public class OsuUserInputManager : UserInputManager { - /// - /// Whether the last input applied to the game is sourced from mouse. - /// - public IBindable LastInputWasMouseSource => lastInputWasMouseSource; - - private readonly Bindable lastInputWasMouseSource = new Bindable(); - internal OsuUserInputManager() { } - public override void HandleInputStateChange(InputStateChangeEvent inputStateChange) - { - switch (inputStateChange) - { - case ButtonStateChangeEvent: - case MousePositionChangeEvent: - lastInputWasMouseSource.Value = true; - break; - - default: - lastInputWasMouseSource.Value = false; - break; - } - - base.HandleInputStateChange(inputStateChange); - } - protected override MouseButtonEventManager CreateButtonEventManagerFor(MouseButton button) { switch (button) From 97c4282e555f73d33a55b06572b5ffd2302a57ba Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 Oct 2022 00:18:50 +0300 Subject: [PATCH 080/321] Add test cases for hiding cursor on keyboard input --- .../Visual/UserInterface/TestSceneCursors.cs | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 6033fc5871..0f238eb547 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { @@ -81,19 +82,18 @@ namespace osu.Game.Tests.Visual.UserInterface }; AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); - - testUserCursor(); - testLocalCursor(); - testUserCursorOverride(); - testMultipleLocalCursors(); } + [SetUp] + public void SetUp() => Schedule(moveOut); + /// /// -- Green Box -- /// Tests whether hovering in and out of a drawable that provides the user cursor (green) /// results in the correct visibility state for that cursor. /// - private void testUserCursor() + [Test] + public void TestUserCursor() { AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); @@ -108,7 +108,8 @@ namespace osu.Game.Tests.Visual.UserInterface /// Tests whether hovering in and out of a drawable that provides a local cursor (purple) /// results in the correct visibility and state for that cursor. /// - private void testLocalCursor() + [Test] + public void TestLocalCursor() { AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); @@ -125,7 +126,8 @@ namespace osu.Game.Tests.Visual.UserInterface /// Tests whether overriding a user cursor (green) with another user cursor (blue) /// results in the correct visibility and states for the cursors. /// - private void testUserCursorOverride() + [Test] + public void TestUserCursorOverride() { AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); @@ -140,7 +142,8 @@ namespace osu.Game.Tests.Visual.UserInterface /// -- Yellow-Purple Box Boundary -- /// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time. /// - private void testMultipleLocalCursors() + [Test] + public void TestMultipleLocalCursors() { AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); @@ -156,7 +159,8 @@ namespace osu.Game.Tests.Visual.UserInterface /// -- Yellow-Blue Box Boundary -- /// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue). /// - private void testUserOverrideWithLocal() + [Test] + public void TestUserOverrideWithLocal() { AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); @@ -168,6 +172,52 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); } + /// + /// Ensures non-mouse input hides global cursor on a "local cursor" area (which doesn't hide global cursor). + /// + [Test] + public void TestKeyboardLocalCursor([Values] bool clickToShow) + { + AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3].ScreenSpaceDrawQuad.Centre + new Vector2(10, 0))); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor alpha is 1", () => globalCursorDisplay.MenuCursor.Alpha == 1); + + AddStep("Press key", () => InputManager.Key(Key.A)); + AddAssert("Check purple cursor still visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddUntilStep("Check global cursor alpha is 0", () => globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); + + if (clickToShow) + AddStep("Click mouse", () => InputManager.Click(MouseButton.Left)); + else + AddStep("Move mouse", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + Vector2.One)); + + AddAssert("Check purple cursor still visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddUntilStep("Check global cursor alpha is 1", () => globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 1); + } + + /// + /// Ensures mouse input after non-mouse input doesn't show global cursor on a "user cursor" area (which hides global cursor). + /// + [Test] + public void TestKeyboardUserCursor([Values] bool clickToShow) + { + AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); + AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor alpha is 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); + + AddStep("Press key", () => InputManager.Key(Key.A)); + AddAssert("Check green cursor still visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor alpha is still 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); + + if (clickToShow) + AddStep("Click mouse", () => InputManager.Click(MouseButton.Left)); + else + AddStep("Move mouse", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + Vector2.One)); + + AddAssert("Check green cursor still visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor alpha is still 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); + } + /// /// Moves the cursor to a point not covered by any cursor containers. /// From 45f590ca433a4e336b99c4d01e716d6c7ade7f20 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 Oct 2022 00:18:56 +0300 Subject: [PATCH 081/321] Add test case for hiding cursor on idle --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index cf62c73ad4..6070b1456f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; +using osuTK.Input; namespace osu.Game.Tests.Visual.Navigation { @@ -79,6 +80,16 @@ namespace osu.Game.Tests.Visual.Navigation [Resolved] private OsuGameBase gameBase { get; set; } + [Test] + public void TestCursorHidesWhenIdle() + { + AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); + AddUntilStep("wait until idle", () => Game.IsIdle.Value); + AddUntilStep("menu cursor hidden", () => Game.GlobalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); + AddStep("click mouse", () => InputManager.Click(MouseButton.Left)); + AddUntilStep("menu cursor shown", () => Game.GlobalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 1); + } + [Test] public void TestNullRulesetHandled() { From 241ecb63a15cc339eda58d2679d54c184be32493 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 Oct 2022 00:19:40 +0300 Subject: [PATCH 082/321] Hide manual input manager cursor in `OsuGameTestScene`s Allows checking that the cursor is actually hidden in `TestSceneOsuGame.TestCursorHidesWhenIdle`. --- osu.Game/Tests/Visual/OsuGameTestScene.cs | 5 ++++ .../Visual/OsuManualInputManagerTestScene.cs | 26 +++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index e47d19fba6..3ca83a4781 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -17,6 +17,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Overlays; @@ -42,6 +43,8 @@ namespace osu.Game.Tests.Visual protected override bool CreateNestedActionContainer => false; + protected override bool DisplayCursorForManualInput => false; + [BackgroundDependencyLoader] private void load() { @@ -119,6 +122,8 @@ namespace osu.Game.Tests.Visual public RealmAccess Realm => Dependencies.Get(); + public new GlobalCursorDisplay GlobalCursorDisplay => base.GlobalCursorDisplay; + public new BackButton BackButton => base.BackButton; public new BeatmapManager BeatmapManager => base.BeatmapManager; diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 9082ca9c58..e56c546bac 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -36,21 +36,31 @@ namespace osu.Game.Tests.Visual /// protected virtual bool CreateNestedActionContainer => true; + /// + /// Whether a menu cursor controlled by the manual input manager should be displayed. + /// True by default, but is disabled for s as they provide their own global cursor. + /// + protected virtual bool DisplayCursorForManualInput => true; + protected OsuManualInputManagerTestScene() { - GlobalCursorDisplay cursorDisplay; + var mainContent = content = new Container { RelativeSizeAxes = Axes.Both }; - CompositeDrawable mainContent = cursorDisplay = new GlobalCursorDisplay { RelativeSizeAxes = Axes.Both }; - - cursorDisplay.Child = content = new OsuTooltipContainer(cursorDisplay.MenuCursor) + if (DisplayCursorForManualInput) { - RelativeSizeAxes = Axes.Both - }; + var cursorDisplay = new GlobalCursorDisplay { RelativeSizeAxes = Axes.Both }; + + cursorDisplay.Add(new OsuTooltipContainer(cursorDisplay.MenuCursor) + { + RelativeSizeAxes = Axes.Both, + Child = mainContent + }); + + mainContent = cursorDisplay; + } if (CreateNestedActionContainer) - { mainContent = new GlobalActionContainer(null).WithChild(mainContent); - } base.Content.AddRange(new Drawable[] { From 421b81b6caaa94aba437e417df924a14faa7744a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 Oct 2022 00:41:14 +0300 Subject: [PATCH 083/321] Fix broken test case --- osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 0f238eb547..6c0191ae27 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestUserOverrideWithLocal() { - AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10, 0))); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); From 91358f4a6aa5a0d7fe41f4473bbd7bb67f261367 Mon Sep 17 00:00:00 2001 From: Aki <75532970+AkiSakurai@users.noreply.github.com> Date: Sat, 15 Oct 2022 15:11:28 +0800 Subject: [PATCH 084/321] Implement tablet button bindings --- .../Settings/Sections/Input/KeyBindingRow.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 2fea2e34b2..c91a6a48d4 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -327,6 +327,50 @@ namespace osu.Game.Overlays.Settings.Sections.Input finalise(); } + protected override bool OnTabletAuxiliaryButtonPress(TabletAuxiliaryButtonPressEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnTabletAuxiliaryButtonRelease(TabletAuxiliaryButtonReleaseEvent e) + { + if (!HasFocus) + { + base.OnTabletAuxiliaryButtonRelease(e); + return; + } + + finalise(); + } + + protected override bool OnTabletPenButtonPress(TabletPenButtonPressEvent e) + { + if (!HasFocus) + return false; + + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + finalise(); + + return true; + } + + protected override void OnTabletPenButtonRelease(TabletPenButtonReleaseEvent e) + { + if (!HasFocus) + { + base.OnTabletPenButtonRelease(e); + return; + } + + finalise(); + } + private void clear() { if (bindTarget == null) From cdea4f879680ccf8f5001716a9e3335c4e59ff04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 Oct 2022 00:05:27 +0900 Subject: [PATCH 085/321] Remove attempted proxying logic in mania implementation --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs index 6a9b3e3aba..e7dfec256d 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon ringExplosion?.PlayAnimation(); } - public Drawable? GetAboveHitObjectsProxiedContent() => JudgementText; + public Drawable? GetAboveHitObjectsProxiedContent() => null; private class RingExplosion : CompositeDrawable { From cd1885a17bbec36fecf2117910f4a8e4c0add095 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 16 Oct 2022 02:55:10 -0700 Subject: [PATCH 086/321] Fix hit error bar icon orientation Icons now keep their original orientation when the hit error bar is flipped --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 747f4d4a8a..9687952fff 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -338,8 +338,26 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters base.Update(); // undo any layout rotation to display icons in the correct orientation - if (labelEarly != null) labelEarly.Rotation = -Rotation; - if (labelLate != null) labelLate.Rotation = -Rotation; + bool xFlipped = Scale.X < 0; + bool yFlipped = Scale.Y < 0; + bool horizontal = Rotation == 90 || Rotation == -90; + + bool flipX = (xFlipped && yFlipped) || (xFlipped && !horizontal) || (yFlipped && horizontal); + bool flipY = (xFlipped && yFlipped) || (xFlipped && horizontal) || (yFlipped && !horizontal); + + Vector2 flipScale = new Vector2(flipX ? -1 : 1, flipY ? -1 : 1); + + if (labelEarly != null) + { + labelEarly.Rotation = -Rotation; + labelEarly.Scale = flipScale; + } + + if (labelLate != null) + { + labelLate.Rotation = -Rotation; + labelLate.Scale = flipScale; + } } private void createColourBars((HitResult result, double length)[] windows) From d6030891a24104dd33e6caf3fc708a1ebc516948 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 16 Oct 2022 10:06:38 -0700 Subject: [PATCH 087/321] Reuse existing code in hit error bar fix Use `UprightAspectMaintainingContainer` instead of a local fix for hit error bar orientation --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 9687952fff..60da967085 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -273,45 +274,74 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters break; case LabelStyles.Icons: - labelEarly = new SpriteIcon + labelEarly = new UprightAspectMaintainingContainer { - Y = -10, - Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.ShippingFast, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, + Y = -10, + Children = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.ShippingFast, + } + } }; - labelLate = new SpriteIcon + labelLate = new UprightAspectMaintainingContainer { - Y = 10, - Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.Bicycle, + AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, + Y = 10, + Children = new Drawable[] + { + new SpriteIcon + { + Y = 10, + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.Bicycle, + } + } }; break; case LabelStyles.Text: - labelEarly = new OsuSpriteText + labelEarly = new UprightAspectMaintainingContainer { - Y = -10, - Text = "Early", - Font = OsuFont.Default.With(size: 10), - Height = 12, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, + Y = -10, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Early", + Font = OsuFont.Default.With(size: 10), + Height = 12, + } + } }; - labelLate = new OsuSpriteText + labelLate = new UprightAspectMaintainingContainer { - Y = 10, - Text = "Late", - Font = OsuFont.Default.With(size: 10), - Height = 12, + AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, + Y = 10, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Late", + Font = OsuFont.Default.With(size: 10), + Height = 12, + } + } }; break; @@ -333,33 +363,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - protected override void Update() - { - base.Update(); - - // undo any layout rotation to display icons in the correct orientation - bool xFlipped = Scale.X < 0; - bool yFlipped = Scale.Y < 0; - bool horizontal = Rotation == 90 || Rotation == -90; - - bool flipX = (xFlipped && yFlipped) || (xFlipped && !horizontal) || (yFlipped && horizontal); - bool flipY = (xFlipped && yFlipped) || (xFlipped && horizontal) || (yFlipped && !horizontal); - - Vector2 flipScale = new Vector2(flipX ? -1 : 1, flipY ? -1 : 1); - - if (labelEarly != null) - { - labelEarly.Rotation = -Rotation; - labelEarly.Scale = flipScale; - } - - if (labelLate != null) - { - labelLate.Rotation = -Rotation; - labelLate.Scale = flipScale; - } - } - private void createColourBars((HitResult result, double length)[] windows) { // max to avoid div-by-zero. From 4a8d976cb4e0be0fbd789d192147e4e8b9ec7735 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sun, 16 Oct 2022 20:20:34 +0300 Subject: [PATCH 088/321] Use api's endpoint --- osu.Game/Overlays/Comments/DrawableComment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 59f6b74722..0675d99a25 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -415,7 +415,7 @@ namespace osu.Game.Overlays.Comments private void copyUrl() { - host.GetClipboard()?.SetText($@"https://osu.ppy.sh/comments/{Comment.Id}"); + host.GetClipboard()?.SetText($@"{api.APIEndpointUrl}/comments/{Comment.Id}"); onScreenDisplay?.Display(new CopyUrlToast()); } From 85de80279382341b5c7392c35b0930eb2dd362a4 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 16 Oct 2022 11:47:21 -0700 Subject: [PATCH 089/321] Only create hit error bar icon containers once Move icon container creation to BDL, and only recreate the contained icon when `recreateLabels` is called --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 104 +++++------------- 1 file changed, 30 insertions(+), 74 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 60da967085..0f5270d644 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -45,8 +45,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters public Bindable LabelStyle { get; } = new Bindable(LabelStyles.Icons); private SpriteIcon arrow; - private Drawable labelEarly; - private Drawable labelLate; + private UprightAspectMaintainingContainer labelEarly; + private UprightAspectMaintainingContainer labelLate; private Container colourBarsEarly; private Container colourBarsLate; @@ -123,6 +123,18 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters RelativeSizeAxes = Axes.Y, Width = judgement_line_width, }, + labelEarly = new UprightAspectMaintainingContainer { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Y = -10, + }, + labelLate = new UprightAspectMaintainingContainer { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Y = 10, + }, } }, arrowContainer = new Container @@ -262,86 +274,39 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { const float icon_size = 14; - labelEarly?.Expire(); - labelEarly = null; - - labelLate?.Expire(); - labelLate = null; - switch (style) { case LabelStyles.None: break; case LabelStyles.Icons: - labelEarly = new UprightAspectMaintainingContainer + labelEarly.Child = new SpriteIcon { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Y = -10, - Children = new Drawable[] - { - new SpriteIcon - { - Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.ShippingFast, - } - } + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.ShippingFast, }; - labelLate = new UprightAspectMaintainingContainer + labelLate.Child = new SpriteIcon { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Y = 10, - Children = new Drawable[] - { - new SpriteIcon - { - Y = 10, - Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.Bicycle, - } - } + Size = new Vector2(icon_size), + Icon = FontAwesome.Solid.Bicycle, }; break; case LabelStyles.Text: - labelEarly = new UprightAspectMaintainingContainer + labelEarly.Child = new OsuSpriteText { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Y = -10, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Early", - Font = OsuFont.Default.With(size: 10), - Height = 12, - } - } + Text = "Early", + Font = OsuFont.Default.With(size: 10), + Height = 12, }; - labelLate = new UprightAspectMaintainingContainer + labelLate.Child = new OsuSpriteText { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Y = 10, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Late", - Font = OsuFont.Default.With(size: 10), - Height = 12, - } - } + Text = "Late", + Font = OsuFont.Default.With(size: 10), + Height = 12, }; break; @@ -350,17 +315,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters throw new ArgumentOutOfRangeException(nameof(style), style, null); } - if (labelEarly != null) - { - colourBars.Add(labelEarly); - labelEarly.FadeInFromZero(500); - } - - if (labelLate != null) - { - colourBars.Add(labelLate); - labelLate.FadeInFromZero(500); - } + labelEarly.FadeInFromZero(500); + labelLate.FadeInFromZero(500); } private void createColourBars((HitResult result, double length)[] windows) From ae05f374a2e79a57373010324fbaea833611b456 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 Oct 2022 03:26:28 +0300 Subject: [PATCH 090/321] Fix potential invalid operation exception in `SubmittingPlayer` token retrieval --- osu.Game/Screens/Play/SubmittingPlayer.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index d56b9c23c8..345bd5a134 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -86,16 +86,13 @@ namespace osu.Game.Screens.Play // Generally a timeout would not happen here as APIAccess will timeout first. if (!tcs.Task.Wait(60000)) - handleTokenFailure(new InvalidOperationException("Token retrieval timed out (request never run)")); + req.TriggerFailure(new InvalidOperationException("Token retrieval timed out (request never run)")); return true; void handleTokenFailure(Exception exception) { - // This method may be invoked multiple times due to the Task.Wait call above. - // We only really care about the first error. - if (!tcs.TrySetResult(false)) - return; + tcs.SetResult(false); if (HandleTokenRetrievalFailure(exception)) { From 1e7b8cd227523acd458c2184d3a35f5ea8d163e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Oct 2022 13:22:06 +0900 Subject: [PATCH 091/321] Fix formatting issues --- .../Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 0f5270d644..d6b9c62369 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -123,13 +123,15 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters RelativeSizeAxes = Axes.Y, Width = judgement_line_width, }, - labelEarly = new UprightAspectMaintainingContainer { + labelEarly = new UprightAspectMaintainingContainer + { AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Y = -10, }, - labelLate = new UprightAspectMaintainingContainer { + labelLate = new UprightAspectMaintainingContainer + { AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, From 28277dd880a20ce80d71efedb5767edda74169ef Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 17 Oct 2022 19:34:05 +0900 Subject: [PATCH 092/321] Fix tests --- .../Multiplayer/TestSceneMultiplayerQueueList.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index f31261dc1f..63677ce378 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -97,14 +97,23 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestCurrentItemDoesNotHaveDeleteButton() + public void TestSingleItemDoesNotHaveDeleteButton() + { + AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); + AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); + + assertDeleteButtonVisibility(0, false); + } + + [Test] + public void TestCurrentItemHasDeleteButtonIfNotSingle() { AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); addPlaylistItem(() => API.LocalUser.Value.OnlineID); - assertDeleteButtonVisibility(0, false); + assertDeleteButtonVisibility(0, true); assertDeleteButtonVisibility(1, true); AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely()); From 1778120f3e2f0bf190aed2860e28f025e8a16cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 12:10:30 +0900 Subject: [PATCH 093/321] Add inline comment mentioning binding issues --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index bc49deedae..54d50b01c4 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -92,6 +92,9 @@ namespace osu.Game.Rulesets.Catch.Edit { switch (e.Action) { + // Note that right now these are hard to use as the default key bindings conflict with existing editor key bindings. + // In the future we will want to expose this via UI and potentially change the key bindings to be editor-specific. + // May be worth considering standardising "zoom" behaviour with what the timeline uses (ie. alt-wheel) but that may cause new conflicts. case GlobalAction.IncreaseScrollSpeed: this.TransformBindableTo(timeRangeMultiplier, timeRangeMultiplier.Value - 1, 200, Easing.OutQuint); break; From 6a7f6c76ffe7758786517cc66a9e582d742130bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 14:31:54 +0900 Subject: [PATCH 094/321] Fix osu! editor padding being applied to toolboxes The result of https://github.com/ppy/osu/pull/20739 and https://github.com/ppy/osu/pull/20738 having unintended side effects when applied together. --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 3 +++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 11 ++++++++++- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 60896b17bf..6b4a6e39d9 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -60,6 +60,9 @@ namespace osu.Game.Rulesets.Osu.Edit [BackgroundDependencyLoader] private void load() { + // Give a bit of breathing room around the playfield content. + PlayfieldContentContainer.Padding = new MarginPadding(10); + LayerBelowRuleset.AddRange(new Drawable[] { distanceSnapGridContainer = new Container diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 63f0c64c80..3bed835854 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Edit InternalChildren = new Drawable[] { - new Container + PlayfieldContentContainer = new Container { Name = "Content", RelativeSizeAxes = Axes.Both, @@ -166,6 +166,15 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged; } + /// + /// Houses all content relevant to the playfield. + /// + /// + /// Generally implementations should not be adding to this directly. + /// Use or instead. + /// + protected Container PlayfieldContentContainer { get; private set; } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index bcb084fd4e..b3aafb9730 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -106,7 +106,6 @@ namespace osu.Game.Screens.Edit Name = "Main content", RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, - Padding = new MarginPadding(10), Child = spinner = new LoadingSpinner(true) { State = { Value = Visibility.Visible }, From 860214c22a11fb5107161c5bb59bb6a6ba734a02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 14:38:45 +0900 Subject: [PATCH 095/321] Adjust paddings to feel better now that backgrounds are visible of toolboxes --- osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs index d3371d3543..26dd5dfa55 100644 --- a/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs +++ b/osu.Game/Rulesets/Edit/ExpandingToolboxContainer.cs @@ -19,7 +19,8 @@ namespace osu.Game.Rulesets.Edit { RelativeSizeAxes = Axes.Y; - FillFlow.Spacing = new Vector2(10); + FillFlow.Spacing = new Vector2(5); + Padding = new MarginPadding { Vertical = 5 }; } protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && anyToolboxHovered(screenSpacePos); From 6b42f1931acf432dcc058298d9520229aeb33a12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 15:01:25 +0900 Subject: [PATCH 096/321] Remove autosize easing in `SettingsToolboxGroup` It just feels like unnecessary motion. --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 04c9a626b5..6dd9e2a56d 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -40,8 +40,6 @@ namespace osu.Game.Overlays Anchor = Anchor.TopCentre, Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, - AutoSizeDuration = transition_duration, - AutoSizeEasing = Easing.OutQuint, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Top = 5, Bottom = 10 }, Spacing = new Vector2(0, 15), From fbad33bef701c45917e633d4863663be9f3c3274 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 15:01:53 +0900 Subject: [PATCH 097/321] Adjust durations and make start off screen --- osu.Game/Screens/Play/PlayerLoader.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b553818a2d..4ff5083107 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,6 +229,9 @@ namespace osu.Game.Screens.Play Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); + // Start off-screen. + settingsScroll.MoveToX(settingsScroll.DrawWidth); + content.ScaleTo(0.7f); contentIn(); @@ -404,10 +407,12 @@ namespace osu.Game.Screens.Play { MetadataInfo.Loading = true; - settingsScroll.FadeInFromZero(400); - content.FadeInFromZero(400); content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer); + + settingsScroll.FadeInFromZero(500, Easing.Out) + .MoveToX(0, 500, Easing.OutQuint); + lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint); highPassFilter.CutoffTo(300).Then().CutoffTo(0, 1250); // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in) @@ -421,7 +426,10 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, CONTENT_OUT_DURATION * 2, Easing.OutQuint); content.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint); - settingsScroll.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint); + + settingsScroll.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint) + .MoveToX(settingsScroll.DrawWidth, CONTENT_OUT_DURATION * 2, Easing.OutQuint); + lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, CONTENT_OUT_DURATION); highPassFilter.CutoffTo(0, CONTENT_OUT_DURATION); } From ec3761ced9122bdf5d5ab0b79a1de6de4ae8893c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 16:01:04 +0900 Subject: [PATCH 098/321] Standardise control point search logic in `OverlappingScrollAlgorithm` Was using a very local algorithm which I cannot guarantee is correct. I'd rather it just use the one used everywhere else. --- .../Gameplay/TestSceneScrollingHitObjects.cs | 4 +-- .../Beatmaps/ControlPoints/ControlPoint.cs | 5 +--- .../ControlPoints/ControlPointInfo.cs | 8 +++--- .../Beatmaps/ControlPoints/IControlPoint.cs | 13 ++++++++++ .../Rulesets/Timing/MultiplierControlPoint.cs | 12 ++++----- .../Algorithms/OverlappingScrollAlgorithm.cs | 26 +++---------------- .../Algorithms/SequentialScrollAlgorithm.cs | 6 ++--- .../UI/Scrolling/DrawableScrollingRuleset.cs | 4 +-- 8 files changed, 35 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Beatmaps/ControlPoints/IControlPoint.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs index 156a1ee34a..6d036f8e9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs @@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void addControlPoints(IList controlPoints, double sequenceStartTime) { - controlPoints.ForEach(point => point.StartTime += sequenceStartTime); + controlPoints.ForEach(point => point.Time += sequenceStartTime); scrollContainers.ForEach(container => { @@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (var playfield in playfields) { foreach (var controlPoint in controlPoints) - playfield.Add(createDrawablePoint(playfield, controlPoint.StartTime)); + playfield.Add(createDrawablePoint(playfield, controlPoint.Time)); } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 56a432aec4..0a09e6e7e6 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -9,11 +9,8 @@ using osuTK.Graphics; namespace osu.Game.Beatmaps.ControlPoints { - public abstract class ControlPoint : IComparable, IDeepCloneable, IEquatable + public abstract class ControlPoint : IComparable, IDeepCloneable, IEquatable, IControlPoint { - /// - /// The time at which the control point takes effect. - /// [JsonIgnore] public double Time { get; set; } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 4be6b5eede..cfe3c671ac 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -196,8 +196,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. /// The active control point at , or a fallback if none found. - protected T BinarySearchWithFallback(IReadOnlyList list, double time, T fallback) - where T : ControlPoint + internal static T BinarySearchWithFallback(IReadOnlyList list, double time, T fallback) + where T : class, IControlPoint { return BinarySearch(list, time) ?? fallback; } @@ -208,8 +208,8 @@ namespace osu.Game.Beatmaps.ControlPoints /// The list to search. /// The time to find the control point at. /// The active control point at . - protected virtual T BinarySearch(IReadOnlyList list, double time) - where T : ControlPoint + internal static T BinarySearch(IReadOnlyList list, double time) + where T : class, IControlPoint { if (list == null) throw new ArgumentNullException(nameof(list)); diff --git a/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs new file mode 100644 index 0000000000..6a287285d8 --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/IControlPoint.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.Beatmaps.ControlPoints +{ + public interface IControlPoint + { + /// + /// The time at which the control point takes effect. + /// + public double Time { get; } + } +} diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 1e80bd165b..279de2f940 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -11,12 +11,12 @@ namespace osu.Game.Rulesets.Timing /// /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier. /// - public class MultiplierControlPoint : IComparable + public class MultiplierControlPoint : IComparable, IControlPoint { /// /// The time in milliseconds at which this starts. /// - public double StartTime; + public double Time { get; set; } /// /// The aggregate multiplier which this provides. @@ -54,13 +54,13 @@ namespace osu.Game.Rulesets.Timing /// /// Creates a . /// - /// The start time of this . - public MultiplierControlPoint(double startTime) + /// The start time of this . + public MultiplierControlPoint(double time) { - StartTime = startTime; + Time = time; } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); + public int CompareTo(MultiplierControlPoint other) => Time.CompareTo(other?.Time); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index d41117bce8..8fd7677a52 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -5,21 +5,18 @@ using System; using osu.Framework.Lists; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public class OverlappingScrollAlgorithm : IScrollAlgorithm { - private readonly MultiplierControlPoint searchPoint; - private readonly SortedList controlPoints; public OverlappingScrollAlgorithm(SortedList controlPoints) { this.controlPoints = controlPoints; - - searchPoint = new MultiplierControlPoint(); } public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength) @@ -52,7 +49,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms for (; i < controlPoints.Count; i++) { float lastPos = pos; - pos = PositionAt(controlPoints[i].StartTime, currentTime, timeRange, scrollLength); + pos = PositionAt(controlPoints[i].Time, currentTime, timeRange, scrollLength); if (pos > position) { @@ -64,7 +61,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms i = Math.Clamp(i, 0, controlPoints.Count - 1); - return controlPoints[i].StartTime + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength; + return controlPoints[i].Time + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength; } public void Reset() @@ -76,21 +73,6 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// /// The time which the should affect. /// The . - private MultiplierControlPoint controlPointAt(double time) - { - if (controlPoints.Count == 0) - return new MultiplierControlPoint(double.NegativeInfinity); - - if (time < controlPoints[0].StartTime) - return controlPoints[0]; - - searchPoint.StartTime = time; - int index = controlPoints.BinarySearch(searchPoint); - - if (index < 0) - index = ~index - 1; - - return controlPoints[index]; - } + private MultiplierControlPoint controlPointAt(double time) => ControlPointInfo.BinarySearch(controlPoints, time) ?? new MultiplierControlPoint(double.NegativeInfinity); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index bfddc22573..8d43185eac 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms if (controlPoints.Count == 0) return; - positionMappings.Add(new PositionMapping(controlPoints[0].StartTime, controlPoints[0])); + positionMappings.Add(new PositionMapping(controlPoints[0].Time, controlPoints[0])); for (int i = 0; i < controlPoints.Count - 1; i++) { @@ -129,9 +129,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms var next = controlPoints[i + 1]; // Figure out how much of the time range the duration represents, and adjust it by the speed multiplier - float length = (float)((next.StartTime - current.StartTime) / timeRange * current.Multiplier); + float length = (float)((next.Time - current.Time) / timeRange * current.Multiplier); - positionMappings.Add(new PositionMapping(next.StartTime, next, positionMappings[^1].Position + length)); + positionMappings.Add(new PositionMapping(next.Time, next, positionMappings[^1].Position + length)); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 825aba5bc2..68469d083c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -158,9 +158,9 @@ namespace osu.Game.Rulesets.UI.Scrolling // Trim unwanted sequences of timing changes timingChanges = timingChanges // Collapse sections after the last hit object - .Where(s => s.StartTime <= lastObjectTime) + .Where(s => s.Time <= lastObjectTime) // Collapse sections with the same start time - .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime); + .GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time); ControlPoints.AddRange(timingChanges); From d237c818f6d744f4a92697f7b45cbc6ff7747dc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 16:15:21 +0900 Subject: [PATCH 099/321] Fix nested objects in overlapping scrolling hit object container ruleset not using correct reference time --- .../UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs | 2 +- .../UI/Scrolling/Algorithms/IScrollAlgorithm.cs | 5 +++-- .../Scrolling/Algorithms/OverlappingScrollAlgorithm.cs | 4 ++-- .../Scrolling/Algorithms/SequentialScrollAlgorithm.cs | 2 +- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 10 +++++----- osu.Game/Tests/Visual/ScrollingTestContainer.cs | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs index 0bd8aa64c9..c957a84eb1 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms return -PositionAt(startTime, endTime, timeRange, scrollLength); } - public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null) => (float)((time - currentTime) / timeRange * scrollLength); public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs index d2fb9e3531..f78509f919 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs @@ -53,8 +53,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// The current time. /// The amount of visible time. /// The absolute spatial length through . + /// The time to be used for control point lookups (ie. the parent's start time for nested hit objects). /// The absolute spatial position. - float PositionAt(double time, double currentTime, double timeRange, float scrollLength); + float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null); /// /// Computes the time which brings a point to a provided spatial position given the current time. @@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// The current time. /// The amount of visible time. /// The absolute spatial length through . - /// The time at which == . + /// The time at which == . double TimeAt(float position, double currentTime, double timeRange, float scrollLength); /// diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index 8fd7677a52..00bc7453d8 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms return -PositionAt(startTime, endTime, timeRange, scrollLength); } - public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) - => (float)((time - currentTime) / timeRange * controlPointAt(time).Multiplier * scrollLength); + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null) + => (float)((time - currentTime) / timeRange * controlPointAt(originTime ?? time).Multiplier * scrollLength); public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) { diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index 8d43185eac..774beb20c7 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms return (float)(objectLength * scrollLength); } - public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null) { double timelineLength = relativePositionAt(time, timeRange) - relativePositionAt(currentTime, timeRange); return (float)(timelineLength * scrollLength); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 37da157cc1..443a37ab1c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -93,9 +93,9 @@ namespace osu.Game.Rulesets.UI.Scrolling /// /// Given a time, return the position along the scrolling axis within this at time . /// - public float PositionAtTime(double time, double currentTime) + public float PositionAtTime(double time, double currentTime, double? originTime = null) { - float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength); + float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength, originTime); return axisInverted ? -scrollPosition : scrollPosition; } @@ -252,14 +252,14 @@ namespace osu.Game.Rulesets.UI.Scrolling updateLayoutRecursive(obj); // Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime - updatePosition(obj, hitObject.HitObject.StartTime); + updatePosition(obj, hitObject.HitObject.StartTime, hitObject.HitObject.StartTime); setComputedLifetimeStart(obj.Entry); } } - private void updatePosition(DrawableHitObject hitObject, double currentTime) + private void updatePosition(DrawableHitObject hitObject, double currentTime, double? parentHitObjectStartTime = null) { - float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime); + float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime, parentHitObjectStartTime); if (scrollingAxis == Direction.Horizontal) hitObject.X = position; diff --git a/osu.Game/Tests/Visual/ScrollingTestContainer.cs b/osu.Game/Tests/Visual/ScrollingTestContainer.cs index cf7fe6e45d..87f4bb3f3b 100644 --- a/osu.Game/Tests/Visual/ScrollingTestContainer.cs +++ b/osu.Game/Tests/Visual/ScrollingTestContainer.cs @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) => implementation.GetLength(startTime, endTime, timeRange, scrollLength); - public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null) => implementation.PositionAt(time, currentTime, timeRange, scrollLength); public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) From 49d5931022de62351712c71d7c53789c2bc45ac1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 Oct 2022 18:39:25 +0900 Subject: [PATCH 100/321] Initial setup with adjustable max combo --- osu.Game.Tests/Gameplay/TestSceneScoring.cs | 138 ++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneScoring.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Gameplay/TestSceneScoring.cs new file mode 100644 index 0000000000..febe6aa123 --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneScoring.cs @@ -0,0 +1,138 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Tests.Visual; +using osuTK.Graphics; + +namespace osu.Game.Tests.Gameplay +{ + public class TestSceneScoring : OsuTestScene + { + private Container graphs = null!; + private SettingsSlider sliderMaxCombo = null!; + + [Test] + public void TestBasic() + { + AddStep("setup tests", () => + { + Children = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + graphs = new Container + { + RelativeSizeAxes = Axes.X, + Height = 200, + }, + }, + new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Full, + Children = new Drawable[] + { + sliderMaxCombo = new SettingsSlider + { + Width = 0.5f, + Current = new BindableInt(1024) + { + MinValue = 96, + MaxValue = 8192, + }, + LabelText = "max combo", + } + } + }, + }, + } + } + }; + + sliderMaxCombo.Current.BindValueChanged(_ => rerun()); + + rerun(); + }); + } + + private ScheduledDelegate? debouncedRun; + + private void rerun() + { + graphs.Clear(); + + debouncedRun?.Cancel(); + debouncedRun = Scheduler.AddDelayed(() => + { + runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); + runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); + }, 200); + } + + private void runForProcessor(string name, ScoreProcessor processor) + { + int maxCombo = sliderMaxCombo.Current.Value; + + var beatmap = new OsuBeatmap(); + + for (int i = 0; i < maxCombo; i++) + { + beatmap.HitObjects.Add(new HitCircle()); + } + + processor.ApplyBeatmap(beatmap); + + int[] missLocations = { 200, 500, 800 }; + + List results = new List(); + + for (int i = 0; i < maxCombo; i++) + { + if (missLocations.Contains(i)) + { + processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Miss + }); + } + else + { + processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Great + }); + } + + results.Add((float)processor.TotalScore.Value); + } + + graphs.Add(new LineGraph + { + RelativeSizeAxes = Axes.Both, + LineColour = Color4.Red, + Values = results + }); + } + } +} From 94c57a459defc4ee6764dae1aa6b7d0fa3fa3f69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Oct 2022 23:27:26 +0900 Subject: [PATCH 101/321] Add ability to add miss locations by clicking --- osu.Game.Tests/Gameplay/TestSceneScoring.cs | 107 ++++++++++++++++++-- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Gameplay/TestSceneScoring.cs index febe6aa123..e18bf5f0bf 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoring.cs @@ -2,12 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Osu; @@ -22,7 +24,7 @@ namespace osu.Game.Tests.Gameplay { public class TestSceneScoring : OsuTestScene { - private Container graphs = null!; + private GraphContainer graphs = null!; private SettingsSlider sliderMaxCombo = null!; [Test] @@ -39,7 +41,7 @@ namespace osu.Game.Tests.Gameplay { new Drawable[] { - graphs = new Container + graphs = new GraphContainer { RelativeSizeAxes = Axes.X, Height = 200, @@ -70,7 +72,10 @@ namespace osu.Game.Tests.Gameplay } }; - sliderMaxCombo.Current.BindValueChanged(_ => rerun()); + sliderMaxCombo.Current.BindValueChanged(_ => rerun(true)); + graphs.MissLocations.BindCollectionChanged((_, __) => rerun()); + + graphs.MaxCombo.BindTo(sliderMaxCombo.Current); rerun(); }); @@ -78,7 +83,7 @@ namespace osu.Game.Tests.Gameplay private ScheduledDelegate? debouncedRun; - private void rerun() + private void rerun(bool debounce = false) { graphs.Clear(); @@ -87,7 +92,7 @@ namespace osu.Game.Tests.Gameplay { runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); - }, 200); + }, debounce ? 200 : 0); } private void runForProcessor(string name, ScoreProcessor processor) @@ -103,13 +108,11 @@ namespace osu.Game.Tests.Gameplay processor.ApplyBeatmap(beatmap); - int[] missLocations = { 200, 500, 800 }; - List results = new List(); for (int i = 0; i < maxCombo; i++) { - if (missLocations.Contains(i)) + if (graphs.MissLocations.Contains(i)) { processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { @@ -135,4 +138,90 @@ namespace osu.Game.Tests.Gameplay }); } } + + public class GraphContainer : Container + { + public readonly BindableList MissLocations = new BindableList(); + + public Bindable MaxCombo = new Bindable(); + + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + + private readonly Box hoverLine; + + private readonly Container missLines; + + public GraphContainer() + { + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.1f), + RelativeSizeAxes = Axes.Both, + }, + Content, + hoverLine = new Box + { + Colour = Color4.Yellow, + RelativeSizeAxes = Axes.Y, + Alpha = 0, + Width = 1, + }, + missLines = new Container + { + RelativeSizeAxes = Axes.Both, + }, + } + }; + + MissLocations.BindCollectionChanged((_, _) => updateMissLocations(), true); + + MaxCombo.BindValueChanged(_ => updateMissLocations()); + } + + private void updateMissLocations() + { + missLines.Clear(); + + foreach (int miss in MissLocations) + { + missLines.Add(new Box + { + Colour = Color4.Red, + Width = 1, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + X = (float)miss / MaxCombo.Value, + }); + } + } + + protected override bool OnHover(HoverEvent e) + { + hoverLine.Show(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hoverLine.Hide(); + base.OnHoverLost(e); + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + hoverLine.X = e.MousePosition.X; + return base.OnMouseMove(e); + } + + protected override bool OnClick(ClickEvent e) + { + MissLocations.Add((int)(e.MousePosition.X / DrawWidth * MaxCombo.Value)); + return true; + } + } } From 1ea2a1ff04bcac4fab4fdeee27d18ccc417d51bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Oct 2022 23:41:07 +0900 Subject: [PATCH 102/321] Add basic legend and line colouring --- osu.Game.Tests/Gameplay/TestSceneScoring.cs | 54 ++++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Gameplay/TestSceneScoring.cs index e18bf5f0bf..95b7868b91 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoring.cs @@ -1,15 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Osu; @@ -27,6 +30,20 @@ namespace osu.Game.Tests.Gameplay private GraphContainer graphs = null!; private SettingsSlider sliderMaxCombo = null!; + private FillFlowContainer legend = null!; + + private static readonly Color4[] line_colours = + { + Color4Extensions.FromHex("588c7e"), + Color4Extensions.FromHex("b2a367"), + Color4Extensions.FromHex("c98f65"), + Color4Extensions.FromHex("bc5151"), + Color4Extensions.FromHex("5c8bd6"), + Color4Extensions.FromHex("7f6ab7"), + Color4Extensions.FromHex("a368ad"), + Color4Extensions.FromHex("aa6880"), + }; + [Test] public void TestBasic() { @@ -48,6 +65,15 @@ namespace osu.Game.Tests.Gameplay }, }, new Drawable[] + { + legend = new FillFlowContainer + { + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + Height = 200, + }, + }, + new Drawable[] { new FillFlowContainer { @@ -58,6 +84,7 @@ namespace osu.Game.Tests.Gameplay sliderMaxCombo = new SettingsSlider { Width = 0.5f, + TransferValueOnCommit = true, Current = new BindableInt(1024) { MinValue = 96, @@ -72,7 +99,7 @@ namespace osu.Game.Tests.Gameplay } }; - sliderMaxCombo.Current.BindValueChanged(_ => rerun(true)); + sliderMaxCombo.Current.BindValueChanged(_ => rerun()); graphs.MissLocations.BindCollectionChanged((_, __) => rerun()); graphs.MaxCombo.BindTo(sliderMaxCombo.Current); @@ -81,22 +108,19 @@ namespace osu.Game.Tests.Gameplay }); } - private ScheduledDelegate? debouncedRun; - - private void rerun(bool debounce = false) + private void rerun() { graphs.Clear(); + legend.Clear(); - debouncedRun?.Cancel(); - debouncedRun = Scheduler.AddDelayed(() => - { - runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); - runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); - }, debounce ? 200 : 0); + runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); + runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); } private void runForProcessor(string name, ScoreProcessor processor) { + Color4 colour = line_colours[Math.Abs(name.GetHashCode()) % line_colours.Length]; + int maxCombo = sliderMaxCombo.Current.Value; var beatmap = new OsuBeatmap(); @@ -133,9 +157,15 @@ namespace osu.Game.Tests.Gameplay graphs.Add(new LineGraph { RelativeSizeAxes = Axes.Both, - LineColour = Color4.Red, + LineColour = colour, Values = results }); + + legend.Add(new OsuSpriteText + { + Colour = colour, + Text = $"{FontAwesome.Solid.Circle.Icon} {name}" + }); } } From 19b4d2d25ed4ed28a9bb4db2db8130da2298079c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Oct 2022 23:49:15 +0900 Subject: [PATCH 103/321] Add vertical grid lines --- osu.Game.Tests/Gameplay/TestSceneScoring.cs | 33 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Gameplay/TestSceneScoring.cs index 95b7868b91..363f1ea758 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoring.cs @@ -180,6 +180,7 @@ namespace osu.Game.Tests.Gameplay private readonly Box hoverLine; private readonly Container missLines; + private readonly Container verticalGridLines; public GraphContainer() { @@ -193,6 +194,10 @@ namespace osu.Game.Tests.Gameplay Colour = OsuColour.Gray(0.1f), RelativeSizeAxes = Axes.Both, }, + verticalGridLines = new Container + { + RelativeSizeAxes = Axes.Both, + }, Content, hoverLine = new Box { @@ -208,9 +213,33 @@ namespace osu.Game.Tests.Gameplay } }; - MissLocations.BindCollectionChanged((_, _) => updateMissLocations(), true); + MissLocations.BindCollectionChanged((_, _) => updateMissLocations()); - MaxCombo.BindValueChanged(_ => updateMissLocations()); + MaxCombo.BindValueChanged(_ => + { + updateMissLocations(); + updateVerticalGridLines(); + }, true); + } + + private void updateVerticalGridLines() + { + verticalGridLines.Clear(); + + for (int i = 0; i < MaxCombo.Value; i++) + { + if (i % 100 == 0) + { + verticalGridLines.Add(new Box + { + Colour = OsuColour.Gray(0.2f), + Width = 1, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + X = (float)i / MaxCombo.Value, + }); + } + } } private void updateMissLocations() From c77847b2844ae8e7f8793a092884c00545b54927 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 16:50:30 +0900 Subject: [PATCH 104/321] Improve layout and add combo text --- osu.Game.Tests/Gameplay/TestSceneScoring.cs | 41 ++++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Gameplay/TestSceneScoring.cs index 363f1ea758..54ead45992 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoring.cs @@ -54,30 +54,38 @@ namespace osu.Game.Tests.Gameplay new GridContainer { RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + }, Content = new[] { new Drawable[] { graphs = new GraphContainer { - RelativeSizeAxes = Axes.X, - Height = 200, + RelativeSizeAxes = Axes.Both, }, }, new Drawable[] { legend = new FillFlowContainer { + Padding = new MarginPadding(20), Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, - Height = 200, + AutoSizeAxes = Axes.Y, }, }, new Drawable[] { new FillFlowContainer { - RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, Children = new Drawable[] { @@ -230,13 +238,26 @@ namespace osu.Game.Tests.Gameplay { if (i % 100 == 0) { - verticalGridLines.Add(new Box + verticalGridLines.AddRange(new Drawable[] { - Colour = OsuColour.Gray(0.2f), - Width = 1, - RelativeSizeAxes = Axes.Y, - RelativePositionAxes = Axes.X, - X = (float)i / MaxCombo.Value, + new Box + { + Colour = OsuColour.Gray(0.2f), + Width = 1, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + X = (float)i / MaxCombo.Value, + }, + new OsuSpriteText + { + RelativePositionAxes = Axes.X, + X = (float)i / MaxCombo.Value, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = $"{i:#,0}", + Rotation = -30, + Y = -20, + } }); } } From d694c8b7711693b53a9bc3db22c420940688a4c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 16:55:04 +0900 Subject: [PATCH 105/321] Move test scene more correctly into visual folder --- osu.Game.Tests/{ => Visual}/Gameplay/TestSceneScoring.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename osu.Game.Tests/{ => Visual}/Gameplay/TestSceneScoring.cs (99%) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs similarity index 99% rename from osu.Game.Tests/Gameplay/TestSceneScoring.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 54ead45992..f2ad97dc03 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -20,10 +20,9 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; -using osu.Game.Tests.Visual; using osuTK.Graphics; -namespace osu.Game.Tests.Gameplay +namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneScoring : OsuTestScene { From 7360cca047baa8ad309c1fe459433a8792647533 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 17:23:23 +0900 Subject: [PATCH 106/321] Add stable v1 algorithm --- .../Visual/Gameplay/TestSceneScoring.cs | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index f2ad97dc03..1cd96a65e9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -122,43 +122,60 @@ namespace osu.Game.Tests.Visual.Gameplay runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); + + int totalScore = 0; + int currentCombo = 0; + + runForAlgorithm("stable-v1", () => + { + const int base_score = 300; + const float score_multiplier = 1; + + totalScore += base_score; + + // combo multiplier + // ReSharper disable once PossibleLossOfFraction + totalScore += (int)(Math.Max(0, currentCombo - 1) * (base_score / 25 * score_multiplier)); + + currentCombo++; + }, () => + { + currentCombo = 0; + }, () => totalScore); } private void runForProcessor(string name, ScoreProcessor processor) { - Color4 colour = line_colours[Math.Abs(name.GetHashCode()) % line_colours.Length]; - int maxCombo = sliderMaxCombo.Current.Value; var beatmap = new OsuBeatmap(); - for (int i = 0; i < maxCombo; i++) - { beatmap.HitObjects.Add(new HitCircle()); - } processor.ApplyBeatmap(beatmap); + runForAlgorithm(name, + () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), + () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), + () => (int)processor.TotalScore.Value); + } + + private void runForAlgorithm(string name, Action applyHit, Action applyMiss, Func getTotalScore) + { + int maxCombo = sliderMaxCombo.Current.Value; + + Color4 colour = line_colours[Math.Abs(name.GetHashCode()) % line_colours.Length]; + List results = new List(); for (int i = 0; i < maxCombo; i++) { if (graphs.MissLocations.Contains(i)) - { - processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Miss - }); - } + applyMiss(); else - { - processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Great - }); - } + applyHit(); - results.Add((float)processor.TotalScore.Value); + results.Add(getTotalScore()); } graphs.Add(new LineGraph From 743ae10df5f9e537605d3431ded469a4d8d74420 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 17:28:13 +0900 Subject: [PATCH 107/321] Improve colouring --- .../Visual/Gameplay/TestSceneScoring.cs | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 1cd96a65e9..d7d5a8f4ab 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -31,18 +31,6 @@ namespace osu.Game.Tests.Visual.Gameplay private FillFlowContainer legend = null!; - private static readonly Color4[] line_colours = - { - Color4Extensions.FromHex("588c7e"), - Color4Extensions.FromHex("b2a367"), - Color4Extensions.FromHex("c98f65"), - Color4Extensions.FromHex("bc5151"), - Color4Extensions.FromHex("5c8bd6"), - Color4Extensions.FromHex("7f6ab7"), - Color4Extensions.FromHex("a368ad"), - Color4Extensions.FromHex("aa6880"), - }; - [Test] public void TestBasic() { @@ -120,13 +108,13 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-classic", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); - runForProcessor("lazer-standardised", new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); + runForProcessor("lazer-standardised", Color4.Cyan, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); + runForProcessor("lazer-classic", Color4.Orange, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); int totalScore = 0; int currentCombo = 0; - runForAlgorithm("stable-v1", () => + runForAlgorithm("stable-v1", Color4.Beige, () => { const int base_score = 300; const float score_multiplier = 1; @@ -144,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, () => totalScore); } - private void runForProcessor(string name, ScoreProcessor processor) + private void runForProcessor(string name, Color4 colour, ScoreProcessor processor) { int maxCombo = sliderMaxCombo.Current.Value; @@ -154,18 +142,16 @@ namespace osu.Game.Tests.Visual.Gameplay processor.ApplyBeatmap(beatmap); - runForAlgorithm(name, + runForAlgorithm(name, colour, () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), () => (int)processor.TotalScore.Value); } - private void runForAlgorithm(string name, Action applyHit, Action applyMiss, Func getTotalScore) + private void runForAlgorithm(string name, Color4 colour, Action applyHit, Action applyMiss, Func getTotalScore) { int maxCombo = sliderMaxCombo.Current.Value; - Color4 colour = line_colours[Math.Abs(name.GetHashCode()) % line_colours.Length]; - List results = new List(); for (int i = 0; i < maxCombo; i++) From 4b2fe72a908d9c20aa2dce1e9d12a7286d767299 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 17:50:51 +0900 Subject: [PATCH 108/321] Add stable v2 algorithm --- .../Visual/Gameplay/TestSceneScoring.cs | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index d7d5a8f4ab..937c52a3cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -109,14 +108,15 @@ namespace osu.Game.Tests.Visual.Gameplay legend.Clear(); runForProcessor("lazer-standardised", Color4.Cyan, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); - runForProcessor("lazer-classic", Color4.Orange, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); + runForProcessor("lazer-classic", Color4.MediumPurple, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); int totalScore = 0; int currentCombo = 0; - runForAlgorithm("stable-v1", Color4.Beige, () => + const int base_score = 300; + + runForAlgorithm("ScoreV1 (classic)", Color4.Beige, () => { - const int base_score = 300; const float score_multiplier = 1; totalScore += base_score; @@ -130,6 +130,43 @@ namespace osu.Game.Tests.Visual.Gameplay { currentCombo = 0; }, () => totalScore); + + double comboPortion = 0; + + int maxCombo = sliderMaxCombo.Current.Value; + + double currentBaseScore = 0; + double maxBaseScore = 0; + + int currentHits = 0; + + double comboPortionMax = 0; + for (int i = 0; i < maxCombo; i++) + comboPortionMax += base_score * (1 + (i + 1) / 10.0); + + runForAlgorithm("ScoreV2", Color4.OrangeRed, () => + { + maxBaseScore += base_score; + currentBaseScore += base_score; + comboPortion += base_score * (1 + ++currentCombo / 10.0); + + currentHits++; + }, () => + { + currentHits++; + maxBaseScore += base_score; + + currentCombo = 0; + }, () => + { + double accuracy = currentBaseScore / maxBaseScore; + + return (int)Math.Round + ( + 700000 * comboPortion / comboPortionMax + + 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) + ); + }); } private void runForProcessor(string name, Color4 colour, ScoreProcessor processor) From 74986e0c8c38c3ba600dc6e1292fe61e67f96165 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 18:05:54 +0900 Subject: [PATCH 109/321] Show final scores and change colouring again --- .../Visual/Gameplay/TestSceneScoring.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 937c52a3cf..5f053e9982 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Gameplay legend = new FillFlowContainer { Padding = new MarginPadding(20), - Direction = FillDirection.Vertical, + Direction = FillDirection.Full, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-standardised", Color4.Cyan, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); + runForProcessor("lazer-standardised", Color4.YellowGreen, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); runForProcessor("lazer-classic", Color4.MediumPurple, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); int totalScore = 0; @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay const int base_score = 300; - runForAlgorithm("ScoreV1 (classic)", Color4.Beige, () => + runForAlgorithm("ScoreV1 (classic)", Color4.Purple, () => { const float score_multiplier = 1; @@ -129,7 +129,13 @@ namespace osu.Game.Tests.Visual.Gameplay }, () => { currentCombo = 0; - }, () => totalScore); + }, () => + { + // Arbitrary value chosen towards the upper range. + const double score_multiplier = 4; + + return (int)(totalScore * score_multiplier); + }); double comboPortion = 0; @@ -211,8 +217,18 @@ namespace osu.Game.Tests.Visual.Gameplay legend.Add(new OsuSpriteText { Colour = colour, + RelativeSizeAxes = Axes.X, + Width = 0.5f, Text = $"{FontAwesome.Solid.Circle.Icon} {name}" }); + + legend.Add(new OsuSpriteText + { + Colour = colour, + RelativeSizeAxes = Axes.X, + Width = 0.5f, + Text = $"final score {getTotalScore():#,0}" + }); } } From a7b3aa62fb4b560c88e95c2be8e318a6e6f9ccf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 18:13:13 +0900 Subject: [PATCH 110/321] Move lines to background to better visualise graphs at points of change --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 5f053e9982..14678bd617 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -85,6 +86,13 @@ namespace osu.Game.Tests.Visual.Gameplay MaxValue = 8192, }, LabelText = "max combo", + }, + new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + Width = 0.5f, + AutoSizeAxes = Axes.Y, + Text = "Left click to add miss" } } }, @@ -261,18 +269,20 @@ namespace osu.Game.Tests.Visual.Gameplay { RelativeSizeAxes = Axes.Both, }, - Content, hoverLine = new Box { Colour = Color4.Yellow, RelativeSizeAxes = Axes.Y, + Origin = Anchor.TopCentre, Alpha = 0, Width = 1, }, missLines = new Container { + Alpha = 0.6f, RelativeSizeAxes = Axes.Both, }, + Content, } }; @@ -298,6 +308,7 @@ namespace osu.Game.Tests.Visual.Gameplay new Box { Colour = OsuColour.Gray(0.2f), + Origin = Anchor.TopCentre, Width = 1, RelativeSizeAxes = Axes.Y, RelativePositionAxes = Axes.X, @@ -327,6 +338,7 @@ namespace osu.Game.Tests.Visual.Gameplay missLines.Add(new Box { Colour = Color4.Red, + Origin = Anchor.TopCentre, Width = 1, RelativeSizeAxes = Axes.Y, RelativePositionAxes = Axes.X, From 74e1b5794bcfd1d30f086d8f999b6b73469a16fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 18:27:05 +0900 Subject: [PATCH 111/321] Add ability to add "OK" or 100s via right click --- .../Visual/Gameplay/TestSceneScoring.cs | 132 +++++++++++++----- 1 file changed, 94 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 14678bd617..f0deeb118b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay { @@ -92,7 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay RelativeSizeAxes = Axes.X, Width = 0.5f, AutoSizeAxes = Axes.Y, - Text = "Left click to add miss" + Text = $"Left click to add miss\nRight click to add OK/{base_ok}" } } }, @@ -102,7 +103,9 @@ namespace osu.Game.Tests.Visual.Gameplay }; sliderMaxCombo.Current.BindValueChanged(_ => rerun()); + graphs.MissLocations.BindCollectionChanged((_, __) => rerun()); + graphs.NonPerfectLocations.BindCollectionChanged((_, __) => rerun()); graphs.MaxCombo.BindTo(sliderMaxCombo.Current); @@ -110,6 +113,9 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + private const int base_great = 300; + private const int base_ok = 100; + private void rerun() { graphs.Clear(); @@ -118,33 +124,50 @@ namespace osu.Game.Tests.Visual.Gameplay runForProcessor("lazer-standardised", Color4.YellowGreen, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Standardised } }); runForProcessor("lazer-classic", Color4.MediumPurple, new ScoreProcessor(new OsuRuleset()) { Mode = { Value = ScoringMode.Classic } }); + runScoreV1(); + runScoreV2(); + } + + private void runScoreV1() + { int totalScore = 0; int currentCombo = 0; - const int base_score = 300; - - runForAlgorithm("ScoreV1 (classic)", Color4.Purple, () => + void applyHitV1(int baseScore) { + if (baseScore == 0) + { + currentCombo = 0; + return; + } + const float score_multiplier = 1; - totalScore += base_score; + totalScore += baseScore; // combo multiplier // ReSharper disable once PossibleLossOfFraction - totalScore += (int)(Math.Max(0, currentCombo - 1) * (base_score / 25 * score_multiplier)); + totalScore += (int)(Math.Max(0, currentCombo - 1) * (baseScore / 25 * score_multiplier)); currentCombo++; - }, () => - { - currentCombo = 0; - }, () => - { - // Arbitrary value chosen towards the upper range. - const double score_multiplier = 4; + } - return (int)(totalScore * score_multiplier); - }); + runForAlgorithm("ScoreV1 (classic)", Color4.Purple, + () => applyHitV1(base_great), + () => applyHitV1(base_ok), + () => applyHitV1(0), + () => + { + // Arbitrary value chosen towards the upper range. + const double score_multiplier = 4; + return (int)(totalScore * score_multiplier); + }); + } + + private void runScoreV2() + { + int currentCombo = 0; double comboPortion = 0; int maxCombo = sliderMaxCombo.Current.Value; @@ -154,33 +177,43 @@ namespace osu.Game.Tests.Visual.Gameplay int currentHits = 0; - double comboPortionMax = 0; for (int i = 0; i < maxCombo; i++) - comboPortionMax += base_score * (1 + (i + 1) / 10.0); + applyHitV2(base_great); - runForAlgorithm("ScoreV2", Color4.OrangeRed, () => + double comboPortionMax = comboPortion; + + comboPortion = 0; + maxBaseScore = 0; + currentBaseScore = 0; + currentHits = 0; + + void applyHitV2(int baseScore) { - maxBaseScore += base_score; - currentBaseScore += base_score; - comboPortion += base_score * (1 + ++currentCombo / 10.0); + maxBaseScore += baseScore; + currentBaseScore += baseScore; + comboPortion += baseScore * (1 + ++currentCombo / 10.0); currentHits++; - }, () => - { - currentHits++; - maxBaseScore += base_score; + } - currentCombo = 0; - }, () => - { - double accuracy = currentBaseScore / maxBaseScore; + runForAlgorithm("ScoreV2", Color4.OrangeRed, + () => applyHitV2(base_great), + () => applyHitV2(base_ok), + () => + { + currentHits++; + maxBaseScore += base_great; + currentCombo = 0; + }, () => + { + double accuracy = currentBaseScore / maxBaseScore; - return (int)Math.Round - ( - 700000 * comboPortion / comboPortionMax + - 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) - ); - }); + return (int)Math.Round + ( + 700000 * comboPortion / comboPortionMax + + 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) + ); + }); } private void runForProcessor(string name, Color4 colour, ScoreProcessor processor) @@ -195,11 +228,12 @@ namespace osu.Game.Tests.Visual.Gameplay runForAlgorithm(name, colour, () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), + () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Ok }), () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), () => (int)processor.TotalScore.Value); } - private void runForAlgorithm(string name, Color4 colour, Action applyHit, Action applyMiss, Func getTotalScore) + private void runForAlgorithm(string name, Color4 colour, Action applyHit, Action applyNonPerfect, Action applyMiss, Func getTotalScore) { int maxCombo = sliderMaxCombo.Current.Value; @@ -209,6 +243,8 @@ namespace osu.Game.Tests.Visual.Gameplay { if (graphs.MissLocations.Contains(i)) applyMiss(); + else if (graphs.NonPerfectLocations.Contains(i)) + applyNonPerfect(); else applyHit(); @@ -243,6 +279,7 @@ namespace osu.Game.Tests.Visual.Gameplay public class GraphContainer : Container { public readonly BindableList MissLocations = new BindableList(); + public readonly BindableList NonPerfectLocations = new BindableList(); public Bindable MaxCombo = new Bindable(); @@ -287,6 +324,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; MissLocations.BindCollectionChanged((_, _) => updateMissLocations()); + NonPerfectLocations.BindCollectionChanged((_, _) => updateMissLocations()); MaxCombo.BindValueChanged(_ => { @@ -345,6 +383,19 @@ namespace osu.Game.Tests.Visual.Gameplay X = (float)miss / MaxCombo.Value, }); } + + foreach (int miss in NonPerfectLocations) + { + missLines.Add(new Box + { + Colour = Color4.Orange, + Origin = Anchor.TopCentre, + Width = 1, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + X = (float)miss / MaxCombo.Value, + }); + } } protected override bool OnHover(HoverEvent e) @@ -365,9 +416,14 @@ namespace osu.Game.Tests.Visual.Gameplay return base.OnMouseMove(e); } - protected override bool OnClick(ClickEvent e) + protected override bool OnMouseDown(MouseDownEvent e) { - MissLocations.Add((int)(e.MousePosition.X / DrawWidth * MaxCombo.Value)); + int combo = (int)(e.MousePosition.X / DrawWidth * MaxCombo.Value); + + if (e.Button == MouseButton.Left) + MissLocations.Add(combo); + else + NonPerfectLocations.Add(combo); return true; } } From d92aca7c22dbd8c6674037ef8078d567f80fad8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 18:30:18 +0900 Subject: [PATCH 112/321] Fix scoreV2 being higher than intended --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index f0deeb118b..7743b44bd4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -167,14 +167,12 @@ namespace osu.Game.Tests.Visual.Gameplay private void runScoreV2() { - int currentCombo = 0; - double comboPortion = 0; - int maxCombo = sliderMaxCombo.Current.Value; + int currentCombo = 0; + double comboPortion = 0; double currentBaseScore = 0; double maxBaseScore = 0; - int currentHits = 0; for (int i = 0; i < maxCombo; i++) @@ -182,9 +180,10 @@ namespace osu.Game.Tests.Visual.Gameplay double comboPortionMax = comboPortion; + currentCombo = 0; comboPortion = 0; - maxBaseScore = 0; currentBaseScore = 0; + maxBaseScore = 0; currentHits = 0; void applyHitV2(int baseScore) From d5666ca7179cef03dda84d989918a48ded3cfc82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 19:01:43 +0900 Subject: [PATCH 113/321] Add tooltip display of current values --- .../Visual/Gameplay/TestSceneScoring.cs | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 7743b44bd4..ec98d00e68 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; @@ -20,6 +22,7 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; +using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -252,6 +255,7 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Add(new LineGraph { + Name = name, RelativeSizeAxes = Axes.Both, LineColour = colour, Values = results @@ -275,7 +279,7 @@ namespace osu.Game.Tests.Visual.Gameplay } } - public class GraphContainer : Container + public class GraphContainer : Container, IHasCustomTooltip> { public readonly BindableList MissLocations = new BindableList(); public readonly BindableList NonPerfectLocations = new BindableList(); @@ -289,6 +293,8 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly Container missLines; private readonly Container verticalGridLines; + public int CurrentHoverCombo { get; private set; } + public GraphContainer() { InternalChild = new Container @@ -411,19 +417,82 @@ namespace osu.Game.Tests.Visual.Gameplay protected override bool OnMouseMove(MouseMoveEvent e) { + CurrentHoverCombo = (int)(e.MousePosition.X / DrawWidth * MaxCombo.Value); + hoverLine.X = e.MousePosition.X; return base.OnMouseMove(e); } protected override bool OnMouseDown(MouseDownEvent e) { - int combo = (int)(e.MousePosition.X / DrawWidth * MaxCombo.Value); - if (e.Button == MouseButton.Left) - MissLocations.Add(combo); + MissLocations.Add(CurrentHoverCombo); else - NonPerfectLocations.Add(combo); + NonPerfectLocations.Add(CurrentHoverCombo); + return true; } + + private GraphTooltip? tooltip; + + public ITooltip> GetCustomTooltip() => tooltip ??= new GraphTooltip(this); + + public IEnumerable TooltipContent => Content.OfType(); + + public class GraphTooltip : CompositeDrawable, ITooltip> + { + private readonly GraphContainer graphContainer; + + private readonly OsuTextFlowContainer textFlow; + + public GraphTooltip(GraphContainer graphContainer) + { + this.graphContainer = graphContainer; + AutoSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 10; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.15f), + RelativeSizeAxes = Axes.Both, + }, + textFlow = new OsuTextFlowContainer + { + Colour = Color4.White, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + } + }; + } + + private int? lastContentCombo; + + public void SetContent(IEnumerable content) + { + int relevantCombo = graphContainer.CurrentHoverCombo; + + if (lastContentCombo == relevantCombo) + return; + + lastContentCombo = relevantCombo; + textFlow.Clear(); + + textFlow.AddParagraph($"At combo {relevantCombo}:"); + + foreach (var graph in content) + { + float valueAtHover = graph.Values.ElementAt(relevantCombo); + float ofTotal = valueAtHover / graph.Values.Last(); + + textFlow.AddParagraph($"{graph.Name}: {valueAtHover:#,0} ({ofTotal * 100:N0}% of final)\n", st => st.Colour = graph.LineColour); + } + } + + public void Move(Vector2 pos) => this.MoveTo(pos); + } } } From f23bc484dc2c925d7405f879be9d4ddd13508e76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Oct 2022 19:15:11 +0900 Subject: [PATCH 114/321] Use better method of positioning mouse over visual settings area --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 5aadd6f56a..917434ae22 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -244,7 +244,10 @@ namespace osu.Game.Tests.Visual.Background public void TestResumeFromPlayer() { performFullSetup(); - AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos)); + AddStep("Move mouse to Visual Settings location", () => InputManager.MoveMouseTo(playerLoader.ScreenSpaceDrawQuad.TopRight + + new Vector2(-playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Width, + playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Height / 2 + ))); AddStep("Resume PlayerLoader", () => player.Restart()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); From e1a8bfa135a103540ad232555fd7f7f33e3eee45 Mon Sep 17 00:00:00 2001 From: Dario Headley Date: Tue, 18 Oct 2022 13:39:40 +0200 Subject: [PATCH 115/321] Fixed approachCircle preempt --- .../Mods/OsuModApproachDifferent.cs | 2 +- .../Mods/OsuModFreezeFrame.cs | 42 +++++++++++++++---- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs b/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs index ec93f19e17..f213d9f193 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle; - public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModFreezeFrame) }; [SettingSource("Initial size", "Change the initial size of the approach circle, relative to hit circles.", 0)] public BindableFloat Scale { get; } = new BindableFloat(4) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index a91d7831c3..f2ee93a704 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -1,17 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; +using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.UI; -using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFreezeFrame : Mod, IApplicableToBeatmap, IApplicableToDrawableRuleset + public class OsuModFreezeFrame : Mod, IApplicableToDrawableHitObject, IApplicableToBeatmap + { public override string Name => "Freeze Frame"; @@ -21,16 +24,18 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Burn the notes into your memory."; + //Alters the transforms of the approach circles, breaking the effects of these mods. + public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent), /*typeof(OsuModHidden)*/ }; + public override ModType Type => ModType.Fun; - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); - } + //mod breaks normal approach circle preempt + private double approachCircleTimePreempt; public void ApplyToBeatmap(IBeatmap beatmap) { double lastNewComboTime = 0; + approachCircleTimePreempt = beatmap.HitObjects.OfType().FirstOrDefault()!.TimePreempt; foreach (var obj in beatmap.HitObjects.OfType()) { @@ -58,5 +63,28 @@ namespace osu.Game.Rulesets.Osu.Mods } } } + + public void ApplyToDrawableHitObject(DrawableHitObject drawableObject) + { + drawableObject.ApplyCustomUpdateState += (drawableHitObject, _) => + { + if (drawableHitObject is not DrawableHitCircle drawableHitCircle) return; + + var hitCircle = drawableHitCircle.HitObject; + var approachCircle = drawableHitCircle.ApproachCircle; + + approachCircle.ClearTransforms(); + approachCircle.ScaleTo(4); + approachCircle.FadeTo(0); + + using (drawableHitCircle.ApproachCircle.BeginAbsoluteSequence(hitCircle.StartTime - approachCircleTimePreempt)) + { + //Redo ApproachCircle animation with correct startTime. + approachCircle.LifetimeStart = hitCircle.StartTime - approachCircleTimePreempt; + approachCircle.FadeTo(1, Math.Min(hitCircle.TimeFadeIn * 2, hitCircle.TimePreempt)); + approachCircle.ScaleTo(1, approachCircleTimePreempt).Then().Expire(); + } + }; + } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 996ee1cddb..2d7c0c82de 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModFreezeFrame) }; public const double FADE_IN_DURATION_MULTIPLIER = 0.4; public const double FADE_OUT_DURATION_MULTIPLIER = 0.3; From acf3498f83a225ddf10bef957370655944f1e2fd Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Tue, 18 Oct 2022 13:49:10 +0200 Subject: [PATCH 116/321] Code Quality --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index f2ee93a704..7395e09d97 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModFreezeFrame : Mod, IApplicableToDrawableHitObject, IApplicableToBeatmap - { public override string Name => "Freeze Frame"; From 764bc1948fffea55f1fd0eb13150949fdbf88c83 Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Tue, 18 Oct 2022 14:59:43 +0200 Subject: [PATCH 117/321] remove commented out Incompatible mod --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 7395e09d97..8bf2c89057 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Burn the notes into your memory."; //Alters the transforms of the approach circles, breaking the effects of these mods. - public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent), /*typeof(OsuModHidden)*/ }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent), typeof(OsuModHidden) }; public override ModType Type => ModType.Fun; From d6ed5612802cbf5b0a5d83e69e6d7fdfe375bcd8 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 19 Oct 2022 00:16:24 +0900 Subject: [PATCH 118/321] make EndPlacement virtual --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index 08e7e57688..863a2c9eac 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints Size = new Vector2(TaikoHitObject.DEFAULT_SIZE * TaikoPlayfield.DEFAULT_HEIGHT) }; } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3bed835854..0266317817 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -359,7 +359,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.PlacementObject.Value = hitObject; } - public void EndPlacement(HitObject hitObject, bool commit) + public virtual void EndPlacement(HitObject hitObject, bool commit) { EditorBeatmap.PlacementObject.Value = null; From 2245456224ceaeb200f9270f1909a25b8e4107e2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Oct 2022 22:22:53 +0300 Subject: [PATCH 119/321] Remove fade easing from legacy hit circles on hit --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 1b2ab82044..f9f9751b6c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -134,10 +134,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - CircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + CircleSprite.FadeOut(legacy_fade_duration); CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out); + OverlaySprite.FadeOut(legacy_fade_duration); OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) @@ -146,11 +146,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (legacyVersion >= 2.0m) // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. - hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out); + hitCircleText.FadeOut(legacy_fade_duration / 4); else { // old skins scale and fade it normally along other pieces. - hitCircleText.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleText.FadeOut(legacy_fade_duration); hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); } } From d46bc7f72af15ec64567ddb7873d814006304198 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Oct 2022 23:43:31 +0300 Subject: [PATCH 120/321] Adjust fade out time to match stable --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index d58a435728..6711c74c9e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateHitStateTransforms(state); - const float fade_out_time = 450; + const float fade_out_time = 240; switch (state) { From 03edfce8223dc57cad38e184e82ce3b992c10478 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Oct 2022 23:43:42 +0300 Subject: [PATCH 121/321] Remove fade easing from sliders --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 6711c74c9e..e37f1133aa 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -341,7 +341,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; } - this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); + this.FadeOut(fade_out_time).Expire(); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos); From 3b3f3dc1598f0e1444bb6e89a99065aa269e4ddb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Oct 2022 23:48:40 +0300 Subject: [PATCH 122/321] Fix new-style spinners fading in later than usual --- .../Skinning/Legacy/LegacyNewStyleSpinner.cs | 4 ++-- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs index 22944becf3..71c3e4c9f0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs @@ -107,8 +107,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn)) + this.FadeInFromZero(spinner.TimeFadeIn); using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt)) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index 004222ad7a..a817e5f2b7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { spin = new Sprite { + Alpha = 0, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Texture = source.GetTexture("spinner-spin"), @@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }, bonusCounter = new LegacySpriteText(LegacyFont.Score) { - Alpha = 0f, + Alpha = 0, Anchor = Anchor.TopCentre, Origin = Anchor.Centre, Scale = new Vector2(SPRITE_SCALE), @@ -179,6 +180,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy spmCounter.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out); } + using (BeginAbsoluteSequence(d.HitObject.StartTime - d.HitObject.TimeFadeIn / 2)) + spin.FadeInFromZero(d.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(d.HitObject.StartTime)) ApproachCircle?.ScaleTo(SPRITE_SCALE * 0.1f, d.HitObject.Duration); From 13e23237914dc6f2d4d5681ecc7920301aa0691c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 Oct 2022 23:48:51 +0300 Subject: [PATCH 123/321] Adjust fade out duration to match stable --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 53f4d21975..6ae9d5bc34 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public readonly IBindable SpinsPerMinute = new BindableDouble(); - private const double fade_out_duration = 160; + private const double fade_out_duration = 240; public DrawableSpinner() : this(null) From a76a0397226b844e923b40e3965a4e745bc0c321 Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:40:43 +0200 Subject: [PATCH 124/321] Rename KiaiFlashingDrawable and move to osu.Game --- .../Skinning/LegacyKiaiFlashingDrawable.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingDrawable.cs => osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs (88%) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingDrawable.cs b/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs similarity index 88% rename from osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingDrawable.cs rename to osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs index 152ed5c3d9..c64f322df9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingDrawable.cs +++ b/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs @@ -7,15 +7,15 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; -namespace osu.Game.Rulesets.Osu.Skinning.Legacy +namespace osu.Game.Skinning { - internal class KiaiFlashingDrawable : BeatSyncedContainer + public class LegacyKiaiFlashingDrawable : BeatSyncedContainer { private readonly Drawable flashingDrawable; private const float flash_opacity = 0.3f; - public KiaiFlashingDrawable(Func creationFunc) + public LegacyKiaiFlashingDrawable(Func creationFunc) { AutoSizeAxes = Axes.Both; From 2b5f12e4fb67ede5e419596ad3d8bc14642bdb2b Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:47:26 +0200 Subject: [PATCH 125/321] Mark existing Drawable test scenes as NUnit for reuse in kiai test scenes --- .../Skinning/TestSceneDrawableDrumRoll.cs | 4 ++-- .../Skinning/TestSceneDrawableHit.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs index 2d27e0e40e..e42dc254ac 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning TimeRange = { Value = 5000 }, }; - [BackgroundDependencyLoader] - private void load() + [Test] + public void DrumrollTest() { AddStep("Drum roll", () => SetContents(_ => { diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index d5a97f8f88..8e9c487c2f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -4,7 +4,6 @@ #nullable disable using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -16,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [TestFixture] public class TestSceneDrawableHit : TaikoSkinnableTestScene { - [BackgroundDependencyLoader] - private void load() + [Test] + public void TestHits() { AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) { From bc57ef061ee32f9a507277ba2128cfab3b4eb25c Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:48:50 +0200 Subject: [PATCH 126/321] Add tests for DrawableHit and DrawableDrumRoll kiai flashing --- .../Skinning/TestSceneDrawableDrumRollKiai.cs | 30 +++++++++++++++++++ .../Skinning/TestSceneDrawableHitKiai.cs | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRollKiai.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHitKiai.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRollKiai.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRollKiai.cs new file mode 100644 index 0000000000..53977150e7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRollKiai.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneDrawableDrumRollKiai : TestSceneDrawableDrumRoll + { + [SetUp] + public void SetUp() => Schedule(() => + { + var controlPointInfo = new ControlPointInfo(); + + controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 }); + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + ControlPointInfo = controlPointInfo + }); + + // track needs to be playing for BeatSyncedContainer to work. + Beatmap.Value.Track.Start(); + }); + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHitKiai.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHitKiai.cs new file mode 100644 index 0000000000..fac0530749 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHitKiai.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneDrawableHitKiai : TestSceneDrawableHit + { + [SetUp] + public void SetUp() => Schedule(() => + { + var controlPointInfo = new ControlPointInfo(); + + controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 }); + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + ControlPointInfo = controlPointInfo + }); + + // track needs to be playing for BeatSyncedContainer to work. + Beatmap.Value.Track.Start(); + }); + } +} From 59213fc00bd2cf10710e26d674c038c1c99def50 Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:53:12 +0200 Subject: [PATCH 127/321] Update LegacyMainCirclePiece to use renamed version of KiaiFlashingDrawable --- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 1b2ab82044..211411591c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - CircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) + CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) }) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Child = OverlaySprite = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) + Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From a21acdb5bc70cbf185f2e2e2447da669c0b2e703 Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:55:02 +0200 Subject: [PATCH 128/321] Implement kiai flashing for legacy taiko skins --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs index 399bd9260d..6bbeb0ed4c 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } // backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer. - AddInternal(backgroundLayer = getDrawableFor("circle")); + AddInternal(backgroundLayer = new LegacyKiaiFlashingDrawable(() => getDrawableFor("circle"))); var foregroundLayer = getDrawableFor("circleoverlay"); if (foregroundLayer != null) From 003e4247f9064e86f2c6bc1aa2eb7baeda7f49ee Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:55:40 +0200 Subject: [PATCH 129/321] Implement kiai flashing for "triangles" taiko skin --- .../Skinning/Default/CirclePiece.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index a7ab1bcd4a..d0eaf9a602 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -32,6 +33,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default private const double pre_beat_transition_time = 80; + private const float flash_opacity = 0.3f; + private Color4 accentColour; /// @@ -157,6 +160,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default if (!effectPoint.KiaiMode) return; + FlashBox + // Make sure the hit indicator usage of FlashBox doesn't get faded out prematurely by a kiai flash + .DelayUntilTransformsFinished() + .FadeTo(flash_opacity, 0, Easing.OutQuint) + .Then() + .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); + if (beatIndex % timingPoint.TimeSignature.Numerator != 0) return; From 9b123e7365a777b11e3de8990b21ad5cfb80820b Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Wed, 19 Oct 2022 00:51:44 +0200 Subject: [PATCH 130/321] Adjust flash intensity and fade values to feel better --- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 2 +- osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index d0eaf9a602..70eacbe61d 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default FlashBox // Make sure the hit indicator usage of FlashBox doesn't get faded out prematurely by a kiai flash .DelayUntilTransformsFinished() - .FadeTo(flash_opacity, 0, Easing.OutQuint) + .FadeTo(flash_opacity) .Then() .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); diff --git a/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs b/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs index c64f322df9..2bcdd5b5a1 100644 --- a/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs +++ b/osu.Game/Skinning/LegacyKiaiFlashingDrawable.cs @@ -13,7 +13,7 @@ namespace osu.Game.Skinning { private readonly Drawable flashingDrawable; - private const float flash_opacity = 0.3f; + private const float flash_opacity = 0.55f; public LegacyKiaiFlashingDrawable(Func creationFunc) { @@ -44,7 +44,7 @@ namespace osu.Game.Skinning flashingDrawable .FadeTo(flash_opacity) .Then() - .FadeOut(timingPoint.BeatLength * 0.75f); + .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); } } } From 343c560b1b92e7e8fb603915816f0f8aad2116de Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 18 Oct 2022 03:33:03 -0700 Subject: [PATCH 131/321] Remove smoke point maximum/cap --- .../Skinning/SmokeSegment.cs | 144 +++++++++--------- 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index aba4d0ff63..6aee9ca2df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -22,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable { - private const int max_point_count = 18_000; - // fade anim values private const double initial_fade_out_duration = 4000; @@ -85,12 +84,6 @@ namespace osu.Game.Rulesets.Osu.Skinning totalDistance = pointInterval; } - private Vector2 nextPointDirection() - { - float angle = RNG.NextSingle(0, 2 * MathF.PI); - return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); - } - public void AddPosition(Vector2 position, double time) { lastPosition ??= position; @@ -107,33 +100,27 @@ namespace osu.Game.Rulesets.Osu.Skinning Vector2 pointPos = (pointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition; increment *= pointInterval; - if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time) - { - int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer()); - SmokePoints.RemoveRange(index, SmokePoints.Count - index); - } - totalDistance %= pointInterval; - for (int i = 0; i < count; i++) + if (SmokePoints.Count == 0 || SmokePoints[^1].Time <= time) { - SmokePoints.Add(new SmokePoint + for (int i = 0; i < count; i++) { - Position = pointPos, - Time = time, - Direction = nextPointDirection(), - }); + SmokePoints.Add(new SmokePoint + { + Position = pointPos, + Time = time, + Angle = RNG.NextSingle(0, 2 * MathF.PI), + }); - pointPos += increment; + pointPos += increment; + } } Invalidate(Invalidation.DrawNode); } lastPosition = position; - - if (SmokePoints.Count >= max_point_count) - FinishDrawing(time); } public void FinishDrawing(double time) @@ -157,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { public Vector2 Position; public double Time; - public Vector2 Direction; + public float Angle; public struct UpperBoundComparer : IComparer { @@ -171,6 +158,17 @@ namespace osu.Game.Rulesets.Osu.Skinning return x.Time > target.Time ? 1 : -1; } } + + public struct LowerBoundComparer : IComparer + { + public int Compare(SmokePoint x, SmokePoint target) + { + // Similar logic as UpperBoundComparer, except returned index will always be + // the first element larger or equal + + return x.Time < target.Time ? -1 : 1; + } + } } protected class SmokeDrawNode : TexturedShaderDrawNode @@ -187,11 +185,11 @@ namespace osu.Game.Rulesets.Osu.Skinning private Vector2 drawSize; private Texture? texture; private int rotationSeed; - private int rotationIndex; + private int firstVisibleIndex; // anim calculation vars (color, scale, direction) private double initialFadeOutDurationTrunc; - private double firstVisiblePointTime; + private double firstVisiblePointTimeAfterSmokeEnded; private double initialFadeOutTime; private double reFadeInTime; @@ -206,9 +204,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.ApplyState(); - points.Clear(); - points.AddRange(Source.SmokePoints); - radius = Source.radius; drawSize = Source.DrawSize; texture = Source.Texture; @@ -220,11 +215,18 @@ namespace osu.Game.Rulesets.Osu.Skinning rotationSeed = Source.rotationSeed; initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime); - firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc; + firstVisiblePointTimeAfterSmokeEnded = SmokeEndTime - initialFadeOutDurationTrunc; - initialFadeOutTime = CurrentTime; - reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed); - finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed); + initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime); + reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / re_fade_in_speed); + finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / final_fade_out_speed); + + double firstVisibleTime = Math.Min(SmokeEndTime, CurrentTime) - initialFadeOutDurationTrunc; + firstVisibleIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = firstVisibleTime }, new SmokePoint.LowerBoundComparer()); + int futureIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer()); + + points.Clear(); + points.AddRange(Source.SmokePoints.Skip(firstVisibleIndex).Take(futureIndex - firstVisibleIndex)); } public sealed override void Draw(IRenderer renderer) @@ -234,9 +236,12 @@ namespace osu.Game.Rulesets.Osu.Skinning if (points.Count == 0) return; - rotationIndex = 0; - - quadBatch ??= renderer.CreateQuadBatch(max_point_count / 10, 10); + quadBatch ??= renderer.CreateQuadBatch(200, 4); + if (points.Count > quadBatch.Size * 4 && quadBatch.Size != 10922) + { + int batchSize = Math.Min((int)(quadBatch.Size * 1.5f), 10922); + quadBatch = renderer.CreateQuadBatch(batchSize, 4); + } texture ??= renderer.WhitePixel; RectangleF textureRect = texture.GetTextureRect(); @@ -248,8 +253,8 @@ namespace osu.Game.Rulesets.Osu.Skinning shader.Bind(); texture.Bind(); - foreach (var point in points) - drawPointQuad(point, textureRect); + for (int i = 0; i < points.Count; i++) + drawPointQuad(points[i], textureRect, i + firstVisibleIndex); shader.Unbind(); renderer.PopLocalMatrix(); @@ -263,30 +268,31 @@ namespace osu.Game.Rulesets.Osu.Skinning { var color = Color4.White; - double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time; - - if (timeDoingInitialFadeOut > 0) + double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; + if (timeDoingFinalFadeOut > 0 && point.Time > firstVisiblePointTimeAfterSmokeEnded) { - float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); - color.A = (1 - fraction) * initial_alpha; + float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); + fraction = MathF.Pow(fraction, 5); + color.A = (1 - fraction) * re_fade_in_alpha; } - - if (color.A > 0) + else { - double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; - double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; - - if (timeDoingFinalFadeOut > 0) + double timeDoingInitialFadeOut = initialFadeOutTime - point.Time; + if (timeDoingInitialFadeOut > 0) { - float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); - fraction = MathF.Pow(fraction, 5); - color.A = (1 - fraction) * re_fade_in_alpha; + float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); + color.A = (1 - fraction) * initial_alpha; } - else if (timeDoingReFadeIn > 0) + + if (point.Time > firstVisiblePointTimeAfterSmokeEnded) { - float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); - fraction = 1 - MathF.Pow(1 - fraction, 5); - color.A = fraction * (re_fade_in_alpha - color.A) + color.A; + double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; + if (timeDoingReFadeIn > 0) + { + float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); + fraction = 1 - MathF.Pow(1 - fraction, 5); + color.A = fraction * (re_fade_in_alpha - color.A) + color.A; + } } } @@ -301,33 +307,33 @@ namespace osu.Game.Rulesets.Osu.Skinning return fraction * (final_scale - initial_scale) + initial_scale; } - protected virtual Vector2 PointDirection(SmokePoint point) + protected virtual Vector2 PointDirection(SmokePoint point, int index) { - float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X); - float finalAngle = initialAngle + nextRotation(); - double timeDoingRotation = CurrentTime - point.Time; float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1); fraction = 1 - MathF.Pow(1 - fraction, 5); - float angle = fraction * (finalAngle - initialAngle) + initialAngle; + float angle = fraction * getRotation(index) + point.Angle; return new Vector2(MathF.Sin(angle), -MathF.Cos(angle)); } - private float nextRotation() => max_rotation * (StatelessRNG.NextSingle(rotationSeed, rotationIndex++) * 2 - 1); + private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1); - private void drawPointQuad(SmokePoint point, RectangleF textureRect) + private void drawPointQuad(SmokePoint point, RectangleF textureRect, int index) { Debug.Assert(quadBatch != null); var colour = PointColour(point); - float scale = PointScale(point); - var dir = PointDirection(point); - var ortho = dir.PerpendicularLeft; - - if (colour.A == 0 || scale == 0) + if (colour.A == 0) return; + float scale = PointScale(point); + if (scale == 0) + return; + + var dir = PointDirection(point, index); + var ortho = dir.PerpendicularLeft; + var localTopLeft = point.Position + (radius * scale * (-ortho - dir)); var localTopRight = point.Position + (radius * scale * (-ortho + dir)); var localBotLeft = point.Position + (radius * scale * (ortho - dir)); From a9b8ba94fa923eb54ca4304504f1e2e6cf416f04 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 18 Oct 2022 22:59:58 -0700 Subject: [PATCH 132/321] Add necessary newlines --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 6aee9ca2df..5e72f165df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -237,11 +237,13 @@ namespace osu.Game.Rulesets.Osu.Skinning return; quadBatch ??= renderer.CreateQuadBatch(200, 4); + if (points.Count > quadBatch.Size * 4 && quadBatch.Size != 10922) { int batchSize = Math.Min((int)(quadBatch.Size * 1.5f), 10922); quadBatch = renderer.CreateQuadBatch(batchSize, 4); } + texture ??= renderer.WhitePixel; RectangleF textureRect = texture.GetTextureRect(); @@ -269,6 +271,7 @@ namespace osu.Game.Rulesets.Osu.Skinning var color = Color4.White; double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; + if (timeDoingFinalFadeOut > 0 && point.Time > firstVisiblePointTimeAfterSmokeEnded) { float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); @@ -278,6 +281,7 @@ namespace osu.Game.Rulesets.Osu.Skinning else { double timeDoingInitialFadeOut = initialFadeOutTime - point.Time; + if (timeDoingInitialFadeOut > 0) { float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1); @@ -287,6 +291,7 @@ namespace osu.Game.Rulesets.Osu.Skinning if (point.Time > firstVisiblePointTimeAfterSmokeEnded) { double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed; + if (timeDoingReFadeIn > 0) { float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1); From 50ab9bff8b8609b1b3438c314d2b957c23578e9d Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Tue, 18 Oct 2022 23:05:09 -0700 Subject: [PATCH 133/321] Rename for consistency --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 5e72f165df..23bfea5240 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private Vector2 drawSize; private Texture? texture; private int rotationSeed; - private int firstVisibleIndex; + private int firstVisiblePointIndex; // anim calculation vars (color, scale, direction) private double initialFadeOutDurationTrunc; @@ -221,12 +221,12 @@ namespace osu.Game.Rulesets.Osu.Skinning reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / re_fade_in_speed); finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / final_fade_out_speed); - double firstVisibleTime = Math.Min(SmokeEndTime, CurrentTime) - initialFadeOutDurationTrunc; - firstVisibleIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = firstVisibleTime }, new SmokePoint.LowerBoundComparer()); - int futureIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer()); + double firstVisiblePointTime = Math.Min(SmokeEndTime, CurrentTime) - initialFadeOutDurationTrunc; + firstVisiblePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = firstVisiblePointTime }, new SmokePoint.LowerBoundComparer()); + int futurePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer()); points.Clear(); - points.AddRange(Source.SmokePoints.Skip(firstVisibleIndex).Take(futureIndex - firstVisibleIndex)); + points.AddRange(Source.SmokePoints.Skip(firstVisiblePointIndex).Take(futurePointIndex - firstVisiblePointIndex)); } public sealed override void Draw(IRenderer renderer) @@ -256,7 +256,7 @@ namespace osu.Game.Rulesets.Osu.Skinning texture.Bind(); for (int i = 0; i < points.Count; i++) - drawPointQuad(points[i], textureRect, i + firstVisibleIndex); + drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex); shader.Unbind(); renderer.PopLocalMatrix(); @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Skinning double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed; - if (timeDoingFinalFadeOut > 0 && point.Time > firstVisiblePointTimeAfterSmokeEnded) + if (timeDoingFinalFadeOut > 0 && point.Time >= firstVisiblePointTimeAfterSmokeEnded) { float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1); fraction = MathF.Pow(fraction, 5); From 9356a40a394f7afcfa95a1325c3b537c7d8a0f0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 15:13:20 +0900 Subject: [PATCH 134/321] Remove redundant flash layer colour logic In a previous iteration, the flash layer was white on the initial hit, but this seems to have been removed for the final implementation. --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index ffdcba3cdb..ca0d99f2f6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -173,11 +173,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon .FadeOut(flash_in_duration); } - // The flash layer starts white to give the wanted brightness, but is almost immediately - // recoloured to the accent colour. This would more correctly be done with two layers (one for the initial flash) - // but works well enough with the colour fade. flash.FadeTo(1, flash_in_duration, Easing.OutQuint); - flash.FlashColour(accentColour.Value, fade_out_time, Easing.OutQuint); this.FadeOut(fade_out_time, Easing.OutQuad); break; From aca0d04834b667bb2893ccb9fbf2f2827fc93254 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 15:52:25 +0900 Subject: [PATCH 135/321] Forcefully remove transforms before reapplying to avoid old accent colour getting rewound --- .../Skinning/Argon/ArgonMainCirclePiece.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index ca0d99f2f6..f5df06f023 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -108,18 +108,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { base.LoadComplete(); - accentColour.BindValueChanged(colour => - { - outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); - outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); - innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); - flash.Colour = colour.NewValue; - }, true); - indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); + accentColour.BindValueChanged(colour => + { + // A colour transform is applied. + // Without removing transforms first, when it is rewound it may apply an old colour. + outerGradient.ClearTransforms(); + outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); + + outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); + innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); + flash.Colour = colour.NewValue; + + updateStateTransforms(drawableObject, drawableObject.State.Value); + }, true); + drawableObject.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableObject, drawableObject.State.Value); } private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) From 59e2478b0ec8e3a4a0ebaf615489224c88b38fd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 16:01:07 +0900 Subject: [PATCH 136/321] Fix some older beatmaps having missing backgrounds Closes https://github.com/ppy/osu/issues/20824. Note that this will require a reimport of beatmaps as it is baked into the database. Probably not worth making a migration for at this point in time. --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 75500fbc4e..5f5749dc73 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -355,6 +355,14 @@ namespace osu.Game.Beatmaps.Formats switch (type) { + case LegacyEventType.Sprite: + // Generally, the background is the first thing defined in a beatmap file. + // In some older beatmaps, it is not present and replaced by a storyboard-level background instead. + // Allow the first sprite (by file order) to act as the background in such cases. + if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); + break; + case LegacyEventType.Background: beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); break; From 830b92d3aebb8a89298504d649baedd27f5e28e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 20:06:39 +0900 Subject: [PATCH 137/321] Add momentary shortcuts to toggle grid/distance snap Matching osu!stable. I use these quite a lot while mapping and I'm sure others do as well. Hold `Shift` = invert grid snap Hold `Alt` = invert distance snap --- .../Edit/CatchHitObjectComposer.cs | 31 ++++++++++++++++ .../Edit/OsuHitObjectComposer.cs | 37 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 54d50b01c4..d40200f2dd 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -26,6 +26,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit { @@ -88,6 +89,36 @@ namespace osu.Game.Rulesets.Catch.Edit updateDistanceSnapGrid(); } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Repeat) + return false; + + if (handleToggleViaKey(e.Key)) + return true; + + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + handleToggleViaKey(e.Key); + base.OnKeyUp(e); + } + + private bool handleToggleViaKey(Key key) + { + switch (key) + { + case Key.AltLeft: + case Key.AltRight: + distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + return true; + } + + return false; + } + public override bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6b4a6e39d9..b9177e9cdd 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -24,6 +25,7 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit { @@ -229,6 +231,41 @@ namespace osu.Game.Rulesets.Osu.Edit } } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Repeat) + return false; + + if (handleToggleViaKey(e.Key)) + return true; + + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + handleToggleViaKey(e.Key); + base.OnKeyUp(e); + } + + private bool handleToggleViaKey(Key key) + { + switch (key) + { + case Key.ShiftLeft: + case Key.ShiftRight: + rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + return true; + + case Key.AltLeft: + case Key.AltRight: + distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + return true; + } + + return false; + } + private DistanceSnapGrid createDistanceSnapGrid(IEnumerable selectedHitObjects) { if (BlueprintContainer.CurrentTool is SpinnerCompositionTool) From bea136ce50fb586619794a3b33f0ccf8337e8942 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 23:56:42 +0900 Subject: [PATCH 138/321] Adjust test assert ordering to read better --- osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index 58b5b41702..1c87eb49c9 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -161,10 +161,11 @@ namespace osu.Game.Tests.Visual.Editing AddStep("hold alt", () => InputManager.PressKey(Key.LAlt)); AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5)); - AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5); AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt)); AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl)); + + AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5); } public class EditorBeatmapContainer : Container From 966dd786ae4460b39ada7d9bf95512114539f261 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 23:59:34 +0900 Subject: [PATCH 139/321] Don't consume keys when handling momentary snap toggles --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index b9177e9cdd..e46131dd6e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -236,8 +236,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (e.Repeat) return false; - if (handleToggleViaKey(e.Key)) - return true; + handleToggleViaKey(e.Key); return base.OnKeyDown(e); } @@ -248,22 +247,20 @@ namespace osu.Game.Rulesets.Osu.Edit base.OnKeyUp(e); } - private bool handleToggleViaKey(Key key) + private void handleToggleViaKey(Key key) { switch (key) { case Key.ShiftLeft: case Key.ShiftRight: rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - return true; + break; case Key.AltLeft: case Key.AltRight: distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - return true; + break; } - - return false; } private DistanceSnapGrid createDistanceSnapGrid(IEnumerable selectedHitObjects) From d83c398b58bdb2d11859dadb96f1c66f6820a890 Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Wed, 19 Oct 2022 23:31:23 +0200 Subject: [PATCH 140/321] Apply review: changed DelayUntilTransformsFinished to ArmedState check --- .../Skinning/Default/CirclePiece.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index 70eacbe61d..36daefbd36 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; using osuTK.Graphics; @@ -155,17 +157,21 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default }; } + [Resolved] + private DrawableHitObject drawableHitObject { get; set; } + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) { if (!effectPoint.KiaiMode) return; - FlashBox - // Make sure the hit indicator usage of FlashBox doesn't get faded out prematurely by a kiai flash - .DelayUntilTransformsFinished() - .FadeTo(flash_opacity) - .Then() - .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); + if (drawableHitObject.State.Value == ArmedState.Idle) + { + FlashBox + .FadeTo(flash_opacity) + .Then() + .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); + } if (beatIndex % timingPoint.TimeSignature.Numerator != 0) return; From 1852714d2d32a329313b6525eec35381f4dbe6ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 01:16:27 +0300 Subject: [PATCH 141/321] Fix existing alpha transform cleared on accent colour change Clearing it causes its start value to be lost. --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index f5df06f023..4b5adaa033 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -114,9 +114,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { // A colour transform is applied. // Without removing transforms first, when it is rewound it may apply an old colour. - outerGradient.ClearTransforms(); + outerGradient.ClearTransforms(targetMember: nameof(Colour)); outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); - outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); flash.Colour = colour.NewValue; From ba37daa456b5239af39abbf428e638058669c706 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 01:18:07 +0300 Subject: [PATCH 142/321] Bring back removed newline --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index 4b5adaa033..36dc8c801d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -116,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon // Without removing transforms first, when it is rewound it may apply an old colour. outerGradient.ClearTransforms(targetMember: nameof(Colour)); outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f)); + outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4); innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); flash.Colour = colour.NewValue; From 78943a21d81fc6658a5be485f354b7f925b4636e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Oct 2022 08:06:05 +0900 Subject: [PATCH 143/321] Update osu.Game/Beatmaps/ControlPoints/IControlPoint.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Beatmaps/ControlPoints/IControlPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs index 6a287285d8..091e99e029 100644 --- a/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/IControlPoint.cs @@ -8,6 +8,6 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time at which the control point takes effect. /// - public double Time { get; } + double Time { get; } } } From a7d4a74ed660366bce2dd1ddef5065363761f595 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Oct 2022 08:07:56 +0900 Subject: [PATCH 144/321] Update osu.Game/Tests/Visual/ScrollingTestContainer.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Tests/Visual/ScrollingTestContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/ScrollingTestContainer.cs b/osu.Game/Tests/Visual/ScrollingTestContainer.cs index 87f4bb3f3b..1817a704b9 100644 --- a/osu.Game/Tests/Visual/ScrollingTestContainer.cs +++ b/osu.Game/Tests/Visual/ScrollingTestContainer.cs @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual => implementation.GetLength(startTime, endTime, timeRange, scrollLength); public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null) - => implementation.PositionAt(time, currentTime, timeRange, scrollLength); + => implementation.PositionAt(time, currentTime, timeRange, scrollLength, originTime); public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) => implementation.TimeAt(position, currentTime, timeRange, scrollLength); From eb386d4bd54cc07957d33a3bd96052d4d68b104a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 03:29:18 +0300 Subject: [PATCH 145/321] Enable slider ball tint in default legacy skin --- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 - osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ osu.Game/Skinning/SkinConfiguration.cs | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 306a1e38b9..1c0a62454b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { SliderBorderSize, SliderPathRadius, - AllowSliderBallTint, CursorCentre, CursorExpand, CursorRotate, diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 04f1286dc7..b80275a1e8 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -46,6 +46,8 @@ namespace osu.Game.Skinning new Color4(242, 24, 57, 255) }; + Configuration.ConfigDictionary[nameof(SkinConfiguration.LegacySetting.AllowSliderBallTint)] = @"true"; + Configuration.LegacyVersion = 2.7m; } } diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 0b1159f8fd..4e5d96ccb8 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -38,7 +38,8 @@ namespace osu.Game.Skinning HitCirclePrefix, HitCircleOverlap, AnimationFramerate, - LayeredHitSounds + LayeredHitSounds, + AllowSliderBallTint, } public static List DefaultComboColours { get; } = new List From 4bf4938b726c584177b28361e6f198afc8878a3e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 03:44:58 +0300 Subject: [PATCH 146/321] Keep cursor hiding feature to gameplay screens for now --- osu.Game/Graphics/Cursor/MenuCursorContainer.cs | 17 ++++++++++++++++- osu.Game/OsuGame.cs | 2 ++ osu.Game/Screens/IOsuScreen.cs | 5 +++++ osu.Game/Screens/OsuScreen.cs | 5 ++--- osu.Game/Screens/Play/Player.cs | 2 ++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index 33b0c308cc..a210cd3063 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -23,6 +23,21 @@ namespace osu.Game.Graphics.Cursor private readonly IBindable screenshotCursorVisibility = new Bindable(true); public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent; + private bool hideCursorOnNonMouseInput; + + public bool HideCursorOnNonMouseInput + { + get => hideCursorOnNonMouseInput; + set + { + if (hideCursorOnNonMouseInput == value) + return; + + hideCursorOnNonMouseInput = value; + updateState(); + } + } + protected override Drawable CreateCursor() => activeCursor = new Cursor(); private Cursor activeCursor = null!; @@ -75,7 +90,7 @@ namespace osu.Game.Graphics.Cursor private void updateState() { - bool combinedVisibility = State.Value == Visibility.Visible && lastInputWasMouse.Value && !isIdle.Value; + bool combinedVisibility = State.Value == Visibility.Visible && (lastInputWasMouse.Value || !hideCursorOnNonMouseInput) && !isIdle.Value; if (visible == combinedVisibility) return; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b3eaf5cd01..2bdcb57f2a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1333,6 +1333,8 @@ namespace osu.Game OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); API.Activity.BindTo(newOsuScreen.Activity); + GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput; + if (newOsuScreen.HideOverlaysOnEnter) CloseAllOverlays(); else diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 7d8657a3df..a5739a41b1 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -41,6 +41,11 @@ namespace osu.Game.Screens /// bool HideOverlaysOnEnter { get; } + /// + /// Whether the menu cursor should be hidden when non-mouse input is received. + /// + bool HideMenuCursorOnNonMouseInput { get; } + /// /// Whether overlays should be able to be opened when this screen is current. /// diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0678a90f71..6be13bbda3 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -40,11 +40,10 @@ namespace osu.Game.Screens public virtual bool AllowExternalScreenChange => false; - /// - /// Whether all overlays should be hidden when this screen is entered or resumed. - /// public virtual bool HideOverlaysOnEnter => false; + public virtual bool HideMenuCursorOnNonMouseInput => false; + /// /// The initial overlay activation mode to use when this screen is entered for the first time. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 68b623b781..7048f83c09 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play public override bool HideOverlaysOnEnter => true; + public override bool HideMenuCursorOnNonMouseInput => true; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; // We are managing our own adjustments (see OnEntering/OnExiting). From 39650717eaa89e838e60b4776bbfa4c08aae3163 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 03:45:17 +0300 Subject: [PATCH 147/321] Improve input detection to not make cursor flicker on combined input --- osu.Game/Graphics/Cursor/MenuCursorContainer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs index a210cd3063..af542989ff 100644 --- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs @@ -277,14 +277,19 @@ namespace osu.Game.Graphics.Cursor { switch (e) { - case MouseEvent: + case MouseDownEvent: + case MouseMoveEvent: lastInputWasMouseSource.Value = true; return false; - default: + case KeyDownEvent keyDown when !keyDown.Repeat: + case JoystickPressEvent: + case MidiDownEvent: lastInputWasMouseSource.Value = false; return false; } + + return false; } } From 7d31eaea549922d3bd8f993f4c3382a19498bee7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 03:32:04 +0300 Subject: [PATCH 148/321] Move ball tinting logic to overwrite `SliderBall` colour --- .../Objects/Drawables/DrawableSlider.cs | 19 ------------------- .../Objects/Drawables/DrawableSliderBall.cs | 10 +--------- .../Skinning/Legacy/LegacySliderBall.cs | 11 +++++++++++ 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index e37f1133aa..785d15c15b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -14,12 +14,10 @@ using osu.Game.Audio; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -106,7 +104,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; - updateBallTint(); }, true); Tracking.BindValueChanged(updateSlidingSample); @@ -257,22 +254,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables SliderBody?.RecyclePath(); } - protected override void ApplySkin(ISkinSource skin, bool allowFallback) - { - base.ApplySkin(skin, allowFallback); - - updateBallTint(); - } - - private void updateBallTint() - { - if (CurrentSkin == null) - return; - - bool allowBallTint = CurrentSkin.GetConfig(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false; - Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White; - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered || Time.Current < HitObject.EndTime) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index a2fe623897..de6ca7dd38 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -11,28 +11,20 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour + public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition { public const float FOLLOW_AREA = 2.4f; public Func GetInitialHitAction; - public Color4 AccentColour - { - get => ball.Colour; - set => ball.Colour = value; - } - private Drawable followCircleReceptor; private DrawableSlider drawableSlider; private Drawable ball; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index 414879f42d..60d71ae843 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject? parentObject { get; set; } + public Color4 BallColour => animationContent.Colour; + private Sprite layerNd = null!; private Sprite layerSpec = null!; @@ -61,6 +64,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }; } + private readonly IBindable accentColour = new Bindable(); + protected override void LoadComplete() { base.LoadComplete(); @@ -69,6 +74,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { parentObject.ApplyCustomUpdateState += updateStateTransforms; updateStateTransforms(parentObject, parentObject.State.Value); + + if (skin.GetConfig(SkinConfiguration.LegacySetting.AllowSliderBallTint)?.Value == true) + { + accentColour.BindTo(parentObject.AccentColour); + accentColour.BindValueChanged(a => animationContent.Colour = a.NewValue, true); + } } } From f444bb9ba6a74a0eeb956104ab22d00e2d29e769 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 03:32:33 +0300 Subject: [PATCH 149/321] Update existing test case --- .../TestSceneSliderApplication.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index 0169627867..728aa27da2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Skinning; using osu.Game.Tests.Visual; using osuTK; @@ -68,10 +69,8 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("create slider", () => { - var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); - tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; - - var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(tintingSkin, Beatmap.Value.Beatmap); + var skin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); + var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(skin, Beatmap.Value.Beatmap); Child = new SkinProvidingContainer(provider) { @@ -92,10 +91,10 @@ namespace osu.Game.Rulesets.Osu.Tests }); AddStep("set accent white", () => dho.AccentColour.Value = Color4.White); - AddAssert("ball is white", () => dho.ChildrenOfType().Single().AccentColour == Color4.White); + AddAssert("ball is white", () => dho.ChildrenOfType().Single().BallColour == Color4.White); AddStep("set accent red", () => dho.AccentColour.Value = Color4.Red); - AddAssert("ball is red", () => dho.ChildrenOfType().Single().AccentColour == Color4.Red); + AddAssert("ball is red", () => dho.ChildrenOfType().Single().BallColour == Color4.Red); } private Slider prepareObject(Slider slider) From 212f15e2d29bfc7435803b5b662fd7a2598dea56 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 Oct 2022 04:06:33 +0300 Subject: [PATCH 150/321] Update existing test cases --- osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs index 6c0191ae27..75c47f0b1b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneCursors.cs @@ -178,6 +178,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestKeyboardLocalCursor([Values] bool clickToShow) { + AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true); AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3].ScreenSpaceDrawQuad.Centre + new Vector2(10, 0))); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check global cursor alpha is 1", () => globalCursorDisplay.MenuCursor.Alpha == 1); @@ -201,6 +202,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestKeyboardUserCursor([Values] bool clickToShow) { + AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true); AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check global cursor alpha is 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0); From a754dc6d3b4d4fc2d02b58f7e68284f92710025a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Oct 2022 14:34:07 +0900 Subject: [PATCH 151/321] Expose binary search methods publicly Co-authored-by: Dan Balasescu --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index cfe3c671ac..9186685f0c 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -196,7 +196,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The time to find the control point at. /// The control point to use when is before any control points. /// The active control point at , or a fallback if none found. - internal static T BinarySearchWithFallback(IReadOnlyList list, double time, T fallback) + public static T BinarySearchWithFallback(IReadOnlyList list, double time, T fallback) where T : class, IControlPoint { return BinarySearch(list, time) ?? fallback; @@ -208,7 +208,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The list to search. /// The time to find the control point at. /// The active control point at . - internal static T BinarySearch(IReadOnlyList list, double time) + public static T BinarySearch(IReadOnlyList list, double time) where T : class, IControlPoint { if (list == null) From 42bb9dc3e3db17b9cb39da3103ce10820a683db9 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Thu, 20 Oct 2022 22:17:26 +0900 Subject: [PATCH 152/321] revert EndPlacement change --- .../Edit/TaikoHitObjectComposer.cs | 12 ------------ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index 06c982397c..57c45fc074 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -19,16 +17,6 @@ namespace osu.Game.Rulesets.Taiko.Edit { } - public override void EndPlacement(HitObject hitObject, bool commit) - { - EditorBeatmap.PlacementObject.Value = null; - - if (commit) - { - EditorBeatmap.Add(hitObject); - } - } - protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] { new HitCompositionTool(), diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 0266317817..3bed835854 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -359,7 +359,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.PlacementObject.Value = hitObject; } - public virtual void EndPlacement(HitObject hitObject, bool commit) + public void EndPlacement(HitObject hitObject, bool commit) { EditorBeatmap.PlacementObject.Value = null; From 26860a903e684db7d3c06241d6889dbcd9186a5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Oct 2022 22:30:30 +0900 Subject: [PATCH 153/321] Refactor implementation to support hitobjects nested multiple levels deep --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 443a37ab1c..424fc7c44c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -236,8 +236,10 @@ namespace osu.Game.Rulesets.UI.Scrolling entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - judgementOffset, computedStartTime); } - private void updateLayoutRecursive(DrawableHitObject hitObject) + private void updateLayoutRecursive(DrawableHitObject hitObject, double? parentHitObjectStartTime = null) { + parentHitObjectStartTime ??= hitObject.HitObject.StartTime; + if (hitObject.HitObject is IHasDuration e) { float length = LengthAtTime(hitObject.HitObject.StartTime, e.EndTime); @@ -249,10 +251,10 @@ namespace osu.Game.Rulesets.UI.Scrolling foreach (var obj in hitObject.NestedHitObjects) { - updateLayoutRecursive(obj); + updateLayoutRecursive(obj, parentHitObjectStartTime); // Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime - updatePosition(obj, hitObject.HitObject.StartTime, hitObject.HitObject.StartTime); + updatePosition(obj, hitObject.HitObject.StartTime, parentHitObjectStartTime); setComputedLifetimeStart(obj.Entry); } } From 5c13c443ff79af12849311737f99ab705b6211ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Oct 2022 23:08:18 +0900 Subject: [PATCH 154/321] Fix incorrect fallback logic Regressed when attempting to share implementation of binary search. --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 2 +- .../Scrolling/Algorithms/OverlappingScrollAlgorithm.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 9186685f0c..422e306450 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -207,7 +207,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The list to search. /// The time to find the control point at. - /// The active control point at . + /// The active control point at . Will return null if there are no control points, or if the time is before the first control point. public static T BinarySearch(IReadOnlyList list, double time) where T : class, IControlPoint { diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index 00bc7453d8..54079c7895 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Linq; using osu.Framework.Lists; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Timing; @@ -73,6 +74,13 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// /// The time which the should affect. /// The . - private MultiplierControlPoint controlPointAt(double time) => ControlPointInfo.BinarySearch(controlPoints, time) ?? new MultiplierControlPoint(double.NegativeInfinity); + private MultiplierControlPoint controlPointAt(double time) + { + return ControlPointInfo.BinarySearch(controlPoints, time) + // The standard binary search will fail if there's no control points, or if the time is before the first. + // For this method, we want to use the first control point in the latter case. + ?? controlPoints.FirstOrDefault() + ?? new MultiplierControlPoint(double.NegativeInfinity); + } } } From bf4a91f1f00b6fab2208416558e142dba6b10caa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 20 Oct 2022 12:48:44 -0700 Subject: [PATCH 155/321] Fix skin toolbox component button not playing hover/click sounds --- osu.Game/Skinning/Editor/SkinComponentToolbox.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index 980dee8601..469657c03c 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -85,10 +85,6 @@ namespace osu.Game.Skinning.Editor { public Action? RequestPlacement; - protected override bool ShouldBeConsideredForInput(Drawable child) => false; - - public override bool PropagateNonPositionalInputSubTree => false; - private readonly Drawable component; private readonly CompositeDrawable? dependencySource; @@ -177,6 +173,10 @@ namespace osu.Game.Skinning.Editor public class DependencyBorrowingContainer : Container { + protected override bool ShouldBeConsideredForInput(Drawable child) => false; + + public override bool PropagateNonPositionalInputSubTree => false; + private readonly CompositeDrawable? donor; public DependencyBorrowingContainer(CompositeDrawable? donor) From 27ce3d8342e0a239042f1402ca486e44461770c6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 Oct 2022 01:52:19 +0300 Subject: [PATCH 156/321] Fix input settings not displaying in visual test browser --- osu.Desktop/OsuGameDesktop.cs | 29 ----------------------------- osu.Game/OsuGameBase.cs | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 3ee1b3da30..09f7292845 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -18,17 +18,9 @@ using osu.Framework; using osu.Framework.Logging; using osu.Game.Updater; using osu.Desktop.Windows; -using osu.Framework.Input.Handlers; -using osu.Framework.Input.Handlers.Joystick; -using osu.Framework.Input.Handlers.Mouse; -using osu.Framework.Input.Handlers.Tablet; -using osu.Framework.Input.Handlers.Touch; using osu.Framework.Threading; using osu.Game.IO; using osu.Game.IPC; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Utils; using SDL2; @@ -148,27 +140,6 @@ namespace osu.Desktop desktopWindow.DragDrop += f => fileDrop(new[] { f }); } - public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler) - { - switch (handler) - { - case ITabletHandler th: - return new TabletSettings(th); - - case MouseHandler mh: - return new MouseSettings(mh); - - case JoystickHandler jh: - return new JoystickSettings(jh); - - case TouchHandler th: - return new InputSection.HandlerSection(th); - - default: - return base.CreateSettingsSubsectionFor(handler); - } - } - protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo(); private readonly List importableFiles = new List(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7d9ed7bf3e..1177f01609 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -21,7 +21,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Handlers; +using osu.Framework.Input.Handlers.Joystick; using osu.Framework.Input.Handlers.Midi; +using osu.Framework.Input.Handlers.Mouse; +using osu.Framework.Input.Handlers.Tablet; +using osu.Framework.Input.Handlers.Touch; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Framework.Platform; @@ -46,6 +50,7 @@ using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; +using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Resources; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -521,6 +526,24 @@ namespace osu.Game /// Should be overriden per-platform to provide settings for platform-specific handlers. public virtual SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler) { + if (RuntimeInfo.IsDesktop) + { + switch (handler) + { + case ITabletHandler th: + return new TabletSettings(th); + + case MouseHandler mh: + return new MouseSettings(mh); + + case JoystickHandler jh: + return new JoystickSettings(jh); + + case TouchHandler: + return new InputSection.HandlerSection(handler); + } + } + switch (handler) { case MidiHandler: From d441e98af785942277e6172c732f512cee626f07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 15:34:25 +0900 Subject: [PATCH 157/321] Adjust kiai flash period in line with stable --- osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index 36daefbd36..6b5a9ae6d2 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; @@ -170,7 +169,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default FlashBox .FadeTo(flash_opacity) .Then() - .FadeOut(Math.Max(80, timingPoint.BeatLength - 80), Easing.OutSine); + .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); } if (beatIndex % timingPoint.TimeSignature.Numerator != 0) From 23b7b9013e65f0b9c7828ac5d1cbac4b18d98dc0 Mon Sep 17 00:00:00 2001 From: Alden Wu Date: Thu, 20 Oct 2022 23:37:05 -0700 Subject: [PATCH 158/321] Change smoke quadbatch growth factor to 2 from 1.5 --- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index 23bfea5240..46c8e7c02a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -238,9 +238,9 @@ namespace osu.Game.Rulesets.Osu.Skinning quadBatch ??= renderer.CreateQuadBatch(200, 4); - if (points.Count > quadBatch.Size * 4 && quadBatch.Size != 10922) + if (points.Count > quadBatch.Size && quadBatch.Size != IRenderer.MAX_QUADS) { - int batchSize = Math.Min((int)(quadBatch.Size * 1.5f), 10922); + int batchSize = Math.Min(quadBatch.Size * 2, IRenderer.MAX_QUADS); quadBatch = renderer.CreateQuadBatch(batchSize, 4); } From 6cdfddea6261a293bf54f285ead6c012eddb1cb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 16:10:55 +0900 Subject: [PATCH 159/321] Always enable distance spacing when adusting distance space multiplier --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 46a827e03a..feedad7f25 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Edit { case GlobalAction.EditorIncreaseDistanceSpacing: case GlobalAction.EditorDecreaseDistanceSpacing: - return adjustDistanceSpacing(e.Action, adjust_step); + return AdjustDistanceSpacing(e.Action, adjust_step); } return false; @@ -127,13 +127,13 @@ namespace osu.Game.Rulesets.Edit { case GlobalAction.EditorIncreaseDistanceSpacing: case GlobalAction.EditorDecreaseDistanceSpacing: - return adjustDistanceSpacing(e.Action, e.ScrollAmount * adjust_step); + return AdjustDistanceSpacing(e.Action, e.ScrollAmount * adjust_step); } return false; } - private bool adjustDistanceSpacing(GlobalAction action, float amount) + protected virtual bool AdjustDistanceSpacing(GlobalAction action, float amount) { if (DistanceSpacingMultiplier.Disabled) return false; From ef990c55ca43efec56f378a205a456749f71ba06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 16:11:19 +0900 Subject: [PATCH 160/321] Handle distance/grid toggles based on key type, rathern than individual left/right --- .../Edit/OsuHitObjectComposer.cs | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index e46131dd6e..4eb1cc3513 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; @@ -25,7 +26,6 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; -using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit { @@ -236,30 +236,61 @@ namespace osu.Game.Rulesets.Osu.Edit if (e.Repeat) return false; - handleToggleViaKey(e.Key); - + handleToggleViaKey(e); return base.OnKeyDown(e); } protected override void OnKeyUp(KeyUpEvent e) { - handleToggleViaKey(e.Key); + handleToggleViaKey(e); base.OnKeyUp(e); } - private void handleToggleViaKey(Key key) + protected override bool AdjustDistanceSpacing(GlobalAction action, float amount) { - switch (key) - { - case Key.ShiftLeft: - case Key.ShiftRight: - rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - break; + // To allow better visualisation, ensure that the spacing grid is visible before adjusting. + distanceSnapToggle.Value = TernaryState.True; - case Key.AltLeft: - case Key.AltRight: + return base.AdjustDistanceSpacing(action, amount); + } + + private TernaryState? gridSnapBeforeMomentary; + private TernaryState? distanceSnapBeforeMomentary; + + private void handleToggleViaKey(KeyboardEvent key) + { + if (key.ShiftPressed) + { + if (gridSnapBeforeMomentary == null) + { + gridSnapBeforeMomentary = rectangularGridSnapToggle.Value; + rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + } + } + else + { + if (gridSnapBeforeMomentary != null) + { + rectangularGridSnapToggle.Value = gridSnapBeforeMomentary.Value; + gridSnapBeforeMomentary = null; + } + } + + if (key.AltPressed) + { + if (distanceSnapBeforeMomentary == null) + { + distanceSnapBeforeMomentary = distanceSnapToggle.Value; distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - break; + } + } + else + { + if (distanceSnapBeforeMomentary != null) + { + distanceSnapToggle.Value = distanceSnapBeforeMomentary.Value; + distanceSnapBeforeMomentary = null; + } } } From 16f5c2a7c6b90dc54baab5407e3b2a06cbf89653 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 17:15:36 +0900 Subject: [PATCH 161/321] Apply same fix to osu!catch composer --- .../Edit/CatchHitObjectComposer.cs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index d40200f2dd..72750fb798 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -94,29 +94,36 @@ namespace osu.Game.Rulesets.Catch.Edit if (e.Repeat) return false; - if (handleToggleViaKey(e.Key)) - return true; - + handleToggleViaKey(e); return base.OnKeyDown(e); } protected override void OnKeyUp(KeyUpEvent e) { - handleToggleViaKey(e.Key); + handleToggleViaKey(e); base.OnKeyUp(e); } - private bool handleToggleViaKey(Key key) - { - switch (key) - { - case Key.AltLeft: - case Key.AltRight: - distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - return true; - } + private TernaryState? distanceSnapBeforeMomentary; - return false; + private void handleToggleViaKey(KeyboardEvent key) + { + if (key.AltPressed) + { + if (distanceSnapBeforeMomentary == null) + { + distanceSnapBeforeMomentary = distanceSnapToggle.Value; + distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + } + } + else + { + if (distanceSnapBeforeMomentary != null) + { + distanceSnapToggle.Value = distanceSnapBeforeMomentary.Value; + distanceSnapBeforeMomentary = null; + } + } } public override bool OnPressed(KeyBindingPressEvent e) From ca91f9f7160517afebbd17eebb9b7c704fa054a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 17:16:55 +0900 Subject: [PATCH 162/321] Don't allow two momentary toggles at the same time to avoid edge cases --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 4eb1cc3513..282631bb1a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Edit { if (key.ShiftPressed) { - if (gridSnapBeforeMomentary == null) + if (distanceSnapBeforeMomentary == null && gridSnapBeforeMomentary == null) { gridSnapBeforeMomentary = rectangularGridSnapToggle.Value; rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; @@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (key.AltPressed) { - if (distanceSnapBeforeMomentary == null) + if (gridSnapBeforeMomentary == null && distanceSnapBeforeMomentary == null) { distanceSnapBeforeMomentary = distanceSnapToggle.Value; distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; From 7b1edff2b36a728e4ef964ea4bf849d86c37d368 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 21 Oct 2022 18:06:38 +0900 Subject: [PATCH 163/321] Add unique hover/select samples to settings sidebar buttons --- osu.Game/Graphics/UserInterface/HoverSampleSet.cs | 3 +++ osu.Game/Overlays/Settings/SidebarButton.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs index 6358317e9d..f0ff76b35d 100644 --- a/osu.Game/Graphics/UserInterface/HoverSampleSet.cs +++ b/osu.Game/Graphics/UserInterface/HoverSampleSet.cs @@ -15,6 +15,9 @@ namespace osu.Game.Graphics.UserInterface [Description("button")] Button, + [Description("button-sidebar")] + ButtonSidebar, + [Description("toolbar")] Toolbar, diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index c6a4cbbcaa..2c4832c68a 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -16,6 +16,11 @@ namespace osu.Game.Overlays.Settings [Resolved] protected OverlayColourProvider ColourProvider { get; private set; } + protected SidebarButton() + : base(HoverSampleSet.ButtonSidebar) + { + } + [BackgroundDependencyLoader] private void load() { From af84f708b7b83d87738fecc45982ec13083c9755 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 19:01:28 +0900 Subject: [PATCH 164/321] Avoid serialising some more properties of `SoloScoreInfo` unless present --- .../Online/API/Requests/Responses/SoloScoreInfo.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index 2c9f250028..4469d50acb 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -114,6 +114,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("has_replay")] public bool HasReplay { get; set; } + // These properties are calculated or not relevant to any external usage. public bool ShouldSerializeID() => false; public bool ShouldSerializeUser() => false; public bool ShouldSerializeBeatmap() => false; @@ -122,6 +123,18 @@ namespace osu.Game.Online.API.Requests.Responses public bool ShouldSerializeOnlineID() => false; public bool ShouldSerializeHasReplay() => false; + // These fields only need to be serialised if they hold values. + // Generally this is required because this model may be used by server-side components, but + // we don't want to bother sending these fields in score submission requests, for instance. + public bool ShouldSerializeEndedAt() => EndedAt != default; + public bool ShouldSerializeStartedAt() => StartedAt != default; + public bool ShouldSerializeLegacyScoreId() => LegacyScoreId != null; + public bool ShouldSerializeLegacyTotalScore() => LegacyTotalScore != null; + public bool ShouldSerializeMods() => Mods.Length > 0; + public bool ShouldSerializeUserID() => UserID > 0; + public bool ShouldSerializeBeatmapID() => BeatmapID > 0; + public bool ShouldSerializeBuildID() => BuildID != null; + #endregion public override string ToString() => $"score_id: {ID} user_id: {UserID}"; From 1e09a212791de25c93c7f48183aacff6f369aa46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 19:55:58 +0900 Subject: [PATCH 165/321] Remove unused using statement --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 72750fb798..27235bd62e 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -26,7 +26,6 @@ using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; -using osuTK.Input; namespace osu.Game.Rulesets.Catch.Edit { From 8b74b5807f3f43b7107ed19dd39636afddada643 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 20:53:37 +0900 Subject: [PATCH 166/321] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3f4c8e2d24..50fb1c4eda 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f1fed6913b..42da129195 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c79d0e4864..d883ce0045 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + From 447d420c997622ec256d7dc8678ee052feb3510c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 21:03:39 +0900 Subject: [PATCH 167/321] Fix adjusting volume via settings playing tick samples twice --- .../Graphics/UserInterface/OsuSliderBar.cs | 5 +++++ .../Settings/Sections/Audio/VolumeSettings.cs | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 2a8b41fd20..9acb0c7f94 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -44,6 +44,8 @@ namespace osu.Game.Graphics.UserInterface public virtual LocalisableString TooltipText { get; private set; } + public bool PlaySamplesOnAdjust { get; set; } = true; + /// /// Whether to format the tooltip as a percentage or the actual value. /// @@ -187,6 +189,9 @@ namespace osu.Game.Graphics.UserInterface private void playSample(T value) { + if (!PlaySamplesOnAdjust) + return; + if (Clock == null || Clock.CurrentTime - lastSampleTime <= 30) return; diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index cea8fdd733..8f188f04d9 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Audio @@ -21,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { Children = new Drawable[] { - new SettingsSlider + new VolumeAdjustSlider { LabelText = AudioSettingsStrings.MasterVolume, Current = audio.Volume, @@ -35,14 +36,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio KeyboardStep = 0.01f, DisplayAsPercentage = true }, - new SettingsSlider + new VolumeAdjustSlider { LabelText = AudioSettingsStrings.EffectVolume, Current = audio.VolumeSample, KeyboardStep = 0.01f, DisplayAsPercentage = true }, - new SettingsSlider + + new VolumeAdjustSlider { LabelText = AudioSettingsStrings.MusicVolume, Current = audio.VolumeTrack, @@ -51,5 +53,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio }, }; } + + private class VolumeAdjustSlider : SettingsSlider + { + protected override Drawable CreateControl() + { + var sliderBar = (OsuSliderBar)base.CreateControl(); + sliderBar.PlaySamplesOnAdjust = false; + return sliderBar; + } + } } } From 7dc03097ffa73297def50e96b8c7609db4ee1246 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 21:51:17 +0900 Subject: [PATCH 168/321] Change distance snap to never account for slider velocity This is a nuanced detail that was implemented incorrectly from the outset. When mapping, generally a mapper chooses the distance spacing with no regard to the SV. It has always been common to have a lower or higher distance spacing than SV, but with the way the lazer editor has worked, the SV was multiplied into the distance snap grid display, incorectly changing its spacing depending on the "reference object" (which is usually the previous hitobject chronologically). --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 46a827e03a..d23d8ad438 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Edit public virtual float GetBeatSnapDistanceAt(HitObject referenceObject) { - return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor); + return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor); } public virtual float DurationToDistance(HitObject referenceObject, double duration) From bace3df4cae732e0b34d518cc0cb3a7cbea1c488 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 22:58:10 +0900 Subject: [PATCH 169/321] Update test assertions in line with change --- .../Editing/TestSceneHitObjectComposerDistanceSnapping.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 1e87ed27df..a98f931e7a 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -71,9 +71,9 @@ namespace osu.Game.Tests.Editing [TestCase(1)] [TestCase(2)] - public void TestSpeedMultiplier(float multiplier) + public void TestSpeedMultiplierDoesNotChangeDistanceSnap(float multiplier) { - assertSnapDistance(100 * multiplier, new HitObject + assertSnapDistance(100, new HitObject { DifficultyControlPoint = new DifficultyControlPoint { From 645a84b1d633973d5e360aec52d4f1693a91baf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 22:58:36 +0900 Subject: [PATCH 170/321] Display current distance snap and add button to update to use it --- .../Edit/CatchHitObjectComposer.cs | 6 ++ .../Edit/OsuHitObjectComposer.cs | 8 ++ .../Edit/DistancedHitObjectComposer.cs | 74 ++++++++++++++++++- osu.Game/Rulesets/Edit/ExpandableButton.cs | 36 +++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/ExpandableButton.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 54d50b01c4..f2eaebbfe5 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -81,6 +81,12 @@ namespace osu.Game.Rulesets.Catch.Edit inputManager = GetContainingInputManager(); } + protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) + { + // TODO: catch lol + return 1; + } + protected override void Update() { base.Update(); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6b4a6e39d9..58d0005ab7 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -112,6 +112,14 @@ namespace osu.Game.Rulesets.Osu.Edit private RectangularPositionSnapGrid rectangularPositionSnapGrid; + protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) + { + float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime()); + float actualDistance = Vector2.Distance(((OsuHitObject)before).EndPosition, ((OsuHitObject)after).Position); + + return actualDistance / expectedDistance; + } + protected override void Update() { base.Update(); diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index d23d8ad438..10be00ac55 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -3,6 +3,8 @@ #nullable disable +using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -13,7 +15,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -32,7 +36,7 @@ namespace osu.Game.Rulesets.Edit { private const float adjust_step = 0.1f; - public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) + public BindableDouble DistanceSpacingMultiplier { get; } = new BindableDouble(1.0) { MinValue = 0.1, MaxValue = 6.0, @@ -44,6 +48,9 @@ namespace osu.Game.Rulesets.Edit protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } private ExpandableSlider> distanceSpacingSlider; + private ExpandableButton readCurrentButton; + + private OsuSpriteText currentDistanceSpacingDisplay; [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } @@ -74,10 +81,29 @@ namespace osu.Game.Rulesets.Edit Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1, Child = new EditorToolboxGroup("snapping") { - Child = distanceSpacingSlider = new ExpandableSlider> + Children = new Drawable[] { - Current = { BindTarget = DistanceSpacingMultiplier }, - KeyboardStep = adjust_step, + distanceSpacingSlider = new ExpandableSlider> + { + Current = { BindTarget = DistanceSpacingMultiplier }, + KeyboardStep = adjust_step, + }, + currentDistanceSpacingDisplay = new OsuSpriteText + { + }, + readCurrentButton = new ExpandableButton + { + Text = "Use current", + Action = () => + { + (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); + + Debug.Assert(objects != null); + + DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + }, + RelativeSizeAxes = Axes.X, + } } } } @@ -85,6 +111,46 @@ namespace osu.Game.Rulesets.Edit }); } + private (HitObject before, HitObject after)? getObjectsOnEitherSideOfCurrentTime() + { + HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.GetEndTime() <= EditorClock.CurrentTime)?.HitObject; + + if (lastBefore == null) + return null; + + HitObject firstAfter = Playfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => h.HitObject.StartTime >= EditorClock.CurrentTime)?.HitObject; + + if (firstAfter == null) + return null; + + if (lastBefore == firstAfter) + return null; + + return (lastBefore, firstAfter); + } + + protected abstract double ReadCurrentDistanceSnap(HitObject before, HitObject after); + + protected override void Update() + { + base.Update(); + + (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); + + if (objects != null) + { + double currentSnap = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + readCurrentButton.Enabled.Value = Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision); + + currentDistanceSpacingDisplay.Text = $"Current {currentSnap:N1}x"; + } + else + { + readCurrentButton.Enabled.Value = false; + currentDistanceSpacingDisplay.Text = "Current: -"; + } + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Rulesets/Edit/ExpandableButton.cs b/osu.Game/Rulesets/Edit/ExpandableButton.cs new file mode 100644 index 0000000000..32885c1a1c --- /dev/null +++ b/osu.Game/Rulesets/Edit/ExpandableButton.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Rulesets.Edit +{ + internal class ExpandableButton : RoundedButton, IExpandable + { + public BindableBool Expanded { get; } = new BindableBool(); + + [Resolved(canBeNull: true)] + private IExpandingContainer? expandingContainer { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + expandingContainer?.Expanded.BindValueChanged(containerExpanded => + { + Expanded.Value = containerExpanded.NewValue; + }, true); + + Expanded.BindValueChanged(expanded => + { + if (expanded.NewValue) + Show(); + else + Hide(); + }, true); + } + } +} From f6de366766b1d22fbc10650ec0feb24aebac71d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 23:21:07 +0900 Subject: [PATCH 171/321] Combine display and button into one control --- .../Edit/DistancedHitObjectComposer.cs | 22 +++--- osu.Game/Rulesets/Edit/ExpandableButton.cs | 69 ++++++++++++++++++- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 10be00ac55..9ef8480b25 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -17,7 +17,6 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -48,9 +47,7 @@ namespace osu.Game.Rulesets.Edit protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; } private ExpandableSlider> distanceSpacingSlider; - private ExpandableButton readCurrentButton; - - private OsuSpriteText currentDistanceSpacingDisplay; + private ExpandableButton currentDistanceSpacingButton; [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } @@ -88,12 +85,8 @@ namespace osu.Game.Rulesets.Edit Current = { BindTarget = DistanceSpacingMultiplier }, KeyboardStep = adjust_step, }, - currentDistanceSpacingDisplay = new OsuSpriteText + currentDistanceSpacingButton = new ExpandableButton { - }, - readCurrentButton = new ExpandableButton - { - Text = "Use current", Action = () => { (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); @@ -140,14 +133,17 @@ namespace osu.Game.Rulesets.Edit if (objects != null) { double currentSnap = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); - readCurrentButton.Enabled.Value = Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision); - currentDistanceSpacingDisplay.Text = $"Current {currentSnap:N1}x"; + currentDistanceSpacingButton.Enabled.Value = currentDistanceSpacingButton.Expanded.Value + && !Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision / 2); + currentDistanceSpacingButton.ContractedLabelText = $"(current {currentSnap:N2}x)"; + currentDistanceSpacingButton.ExpandedLabelText = $"Use current ({currentSnap:N2}x)"; } else { - readCurrentButton.Enabled.Value = false; - currentDistanceSpacingDisplay.Text = "Current: -"; + currentDistanceSpacingButton.Enabled.Value = false; + currentDistanceSpacingButton.ContractedLabelText = "Current N/A"; + currentDistanceSpacingButton.ExpandedLabelText = "Use current (N/A)"; } } diff --git a/osu.Game/Rulesets/Edit/ExpandableButton.cs b/osu.Game/Rulesets/Edit/ExpandableButton.cs index 32885c1a1c..572ae19a03 100644 --- a/osu.Game/Rulesets/Edit/ExpandableButton.cs +++ b/osu.Game/Rulesets/Edit/ExpandableButton.cs @@ -3,6 +3,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterfaceV2; @@ -10,6 +13,54 @@ namespace osu.Game.Rulesets.Edit { internal class ExpandableButton : RoundedButton, IExpandable { + private float actualHeight; + + public override float Height + { + get => base.Height; + set => base.Height = actualHeight = value; + } + + private LocalisableString contractedLabelText; + + /// + /// The label text to display when this slider is in a contracted state. + /// + public LocalisableString ContractedLabelText + { + get => contractedLabelText; + set + { + if (value == contractedLabelText) + return; + + contractedLabelText = value; + + if (!Expanded.Value) + Text = value; + } + } + + private LocalisableString expandedLabelText; + + /// + /// The label text to display when this slider is in an expanded state. + /// + public LocalisableString ExpandedLabelText + { + get => expandedLabelText; + set + { + if (value == expandedLabelText) + return; + + expandedLabelText = value; + + if (Expanded.Value) + Text = value; + } + } + public BindableBool Expanded { get; } = new BindableBool(); [Resolved(canBeNull: true)] @@ -26,10 +77,24 @@ namespace osu.Game.Rulesets.Edit Expanded.BindValueChanged(expanded => { + Text = expanded.NewValue ? expandedLabelText : contractedLabelText; + if (expanded.NewValue) - Show(); + { + SpriteText.Anchor = Anchor.Centre; + SpriteText.Origin = Anchor.Centre; + SpriteText.Font = OsuFont.GetFont(weight: FontWeight.Bold); + base.Height = actualHeight; + Background.Show(); + } else - Hide(); + { + SpriteText.Anchor = Anchor.CentreLeft; + SpriteText.Origin = Anchor.CentreLeft; + SpriteText.Font = OsuFont.GetFont(weight: FontWeight.Regular); + base.Height = SpriteText.DrawHeight; + Background.Hide(); + } }, true); } } From bb921ff9a76e2e608e97b1d6c571454337c00ab6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 23:35:47 +0900 Subject: [PATCH 172/321] Fix incorrect current DS value when inside a slider --- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 9ef8480b25..4a7e0828cd 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Edit private (HitObject before, HitObject after)? getObjectsOnEitherSideOfCurrentTime() { - HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.GetEndTime() <= EditorClock.CurrentTime)?.HitObject; + HitObject lastBefore = Playfield.HitObjectContainer.AliveObjects.LastOrDefault(h => h.HitObject.StartTime <= EditorClock.CurrentTime)?.HitObject; if (lastBefore == null) return null; From f516e32949d2b4c24bad95b2c57c2f95f5ef20dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 23:35:53 +0900 Subject: [PATCH 173/321] Improve UI a bit --- .../Rulesets/Edit/DistancedHitObjectComposer.cs | 15 +++++++++------ osu.Game/Rulesets/Edit/ExpandableButton.cs | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 4a7e0828cd..9a65570e72 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -94,6 +94,7 @@ namespace osu.Game.Rulesets.Edit Debug.Assert(objects != null); DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + // TODO: This should probably also force distance spacing grid on. }, RelativeSizeAxes = Axes.X, } @@ -130,20 +131,22 @@ namespace osu.Game.Rulesets.Edit (HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime(); - if (objects != null) - { - double currentSnap = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + double currentSnap = objects == null + ? 0 + : ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + if (currentSnap > DistanceSpacingMultiplier.MinValue) + { currentDistanceSpacingButton.Enabled.Value = currentDistanceSpacingButton.Expanded.Value && !Precision.AlmostEquals(currentSnap, DistanceSpacingMultiplier.Value, DistanceSpacingMultiplier.Precision / 2); - currentDistanceSpacingButton.ContractedLabelText = $"(current {currentSnap:N2}x)"; + currentDistanceSpacingButton.ContractedLabelText = $"current {currentSnap:N2}x"; currentDistanceSpacingButton.ExpandedLabelText = $"Use current ({currentSnap:N2}x)"; } else { currentDistanceSpacingButton.Enabled.Value = false; - currentDistanceSpacingButton.ContractedLabelText = "Current N/A"; - currentDistanceSpacingButton.ExpandedLabelText = "Use current (N/A)"; + currentDistanceSpacingButton.ContractedLabelText = string.Empty; + currentDistanceSpacingButton.ExpandedLabelText = "Use current (unavailable)"; } } diff --git a/osu.Game/Rulesets/Edit/ExpandableButton.cs b/osu.Game/Rulesets/Edit/ExpandableButton.cs index 572ae19a03..5b60a2536d 100644 --- a/osu.Game/Rulesets/Edit/ExpandableButton.cs +++ b/osu.Game/Rulesets/Edit/ExpandableButton.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Edit SpriteText.Anchor = Anchor.CentreLeft; SpriteText.Origin = Anchor.CentreLeft; SpriteText.Font = OsuFont.GetFont(weight: FontWeight.Regular); - base.Height = SpriteText.DrawHeight; + base.Height = actualHeight / 2; Background.Hide(); } }, true); From c1ed775deb3a14a4344bc0b177b65baaa99f2d4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 23:36:16 +0900 Subject: [PATCH 174/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 50fb1c4eda..b036d3d177 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 42da129195..b64d135ab8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index d883ce0045..06fac451ad 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From 678bfb2caa67d779d052ae70d61401d66bb912bf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 Oct 2022 20:56:18 +0300 Subject: [PATCH 175/321] Ban `char.ToLower()`/`char.ToUpper()` as well for better safety --- CodeAnalysis/BannedSymbols.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 022da0a2ea..03fd21829d 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -15,6 +15,8 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks. M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever. +M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture. +M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture. M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString. M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString. M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead. From 3d37a67590f866bc43a26c69a2be15ca1938279e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Oct 2022 14:15:17 +0900 Subject: [PATCH 176/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b036d3d177..152e050805 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b64d135ab8..bd8c915f5f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 06fac451ad..1a3f9dbe67 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From 9155fcf3cb8cb71eaae196b8962f136c54fc8bd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Oct 2022 23:25:08 +0900 Subject: [PATCH 177/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 152e050805..f251e8ee71 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bd8c915f5f..e0561d9b21 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 1a3f9dbe67..cf70b65578 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From a74e873b983678993ca7d3c4c6d20468d052e9b7 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 23 Oct 2022 01:13:29 +0900 Subject: [PATCH 178/321] remove useless using --- osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs index 57c45fc074..161799c980 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Screens.Edit.Compose.Components; From f14d871f97375bdff5f1eada82facd6b5e8ea6f3 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 23 Oct 2022 01:32:22 +0900 Subject: [PATCH 179/321] let TaikoSpan show in timeline before placed --- .../Edit/Blueprints/TaikoSpanPlacementBlueprint.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index 23a005190a..70364cabf1 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -52,6 +52,12 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints private double originalStartTime; private Vector2 originalPosition; + protected override void LoadComplete() + { + base.LoadComplete(); + BeginPlacement(); + } + protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button != MouseButton.Left) From f4db2c1ff421588f3896f8e26bcb43da082179a7 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Sat, 22 Oct 2022 22:38:04 +0200 Subject: [PATCH 180/321] Add test --- .../TestSceneControlPointConversion.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs new file mode 100644 index 0000000000..e2ec41ce21 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneControlPointConversion : OsuTestScene + { + [Test] + public void TestSceneScrollSpeedConversion() + { + const double start_time = 1000; + const double slider_velocity = 10; + + var beatmap = new Beatmap + { + HitObjects = + { + new HitObject + { + StartTime = start_time, + DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = slider_velocity } + } + }, + BeatmapInfo = { Ruleset = { OnlineID = 0 } }, + }; + + var convertedBeatmap = new TaikoRuleset().CreateBeatmapConverter(beatmap).Convert(); + + AddAssert("effect point generated", () => convertedBeatmap.ControlPointInfo.EffectPointAt(start_time).ScrollSpeed == slider_velocity); + } + } +} From e8960ba53b25c33dae8822598180c3fb189397e8 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Sat, 22 Oct 2022 22:46:19 +0200 Subject: [PATCH 181/321] whitespace removal --- .../TestSceneControlPointConversion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs index e2ec41ce21..72763421cc 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests }; var convertedBeatmap = new TaikoRuleset().CreateBeatmapConverter(beatmap).Convert(); - + AddAssert("effect point generated", () => convertedBeatmap.ControlPointInfo.EffectPointAt(start_time).ScrollSpeed == slider_velocity); } } From a35026d537faf39e902a541f5b6ed284e507aba1 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sat, 22 Oct 2022 23:29:44 +0200 Subject: [PATCH 182/321] Downgrade AutoMapper to fix Android startup crash --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e0561d9b21..22474c0592 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 889c2978d7a95ea864c584db8bd61d81dc615f04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 13:13:40 +0900 Subject: [PATCH 183/321] Fix point conversion not using invariant culture This was only the case in a fallback path (ie. when the user provides a `json` file with an old or computed format from an external source). Closes #20844. --- osu.Game.Tournament/JsonPointConverter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index db48c36c99..d3b40a3526 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using System.Drawing; +using System.Globalization; using Newtonsoft.Json; namespace osu.Game.Tournament @@ -31,7 +32,9 @@ namespace osu.Game.Tournament Debug.Assert(str != null); - return new PointConverter().ConvertFromString(str) as Point? ?? new Point(); + // Null check suppression is required due to .NET standard expecting a non-null context. + // Seems to work fine at a runtime level (and the parameter is nullable in .NET 6+). + return new PointConverter().ConvertFromString(null!, CultureInfo.InvariantCulture, str) as Point? ?? new Point(); } var point = new Point(); From e9b3048a8bca903f35bf483a02a4972c64934fc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 13:58:00 +0900 Subject: [PATCH 184/321] Change the order of global bindings to give overlays lowest priority --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index ad53f6d90f..4a233e5bf3 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -31,14 +31,17 @@ namespace osu.Game.Input.Bindings parentInputManager = GetContainingInputManager(); } - // IMPORTANT: Do not change the order of key bindings in this list. - // It is used to decide the order of precedence (see note in DatabasedKeyBindingContainer). + // IMPORTANT: Take care when changing order of the items in the enumerable. + // It is used to decide the order of precedence, with the earlier items having higher precedence. public override IEnumerable DefaultKeyBindings => GlobalKeyBindings - .Concat(OverlayKeyBindings) .Concat(EditorKeyBindings) .Concat(InGameKeyBindings) .Concat(SongSelectKeyBindings) - .Concat(AudioControlKeyBindings); + .Concat(AudioControlKeyBindings) + // Overlay bindings may conflict with more local cases like the editor so they are checked last. + // It has generally been agreed on that local screens like the editor should have priority, + // based on such usages potentially requiring a lot more key bindings that may be "shared" with global ones. + .Concat(OverlayKeyBindings); public IEnumerable GlobalKeyBindings => new[] { From e72a71a28e9d25f0133592a8a24af3fe29fe3318 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 13:58:11 +0900 Subject: [PATCH 185/321] Add simple editor "duplicate objects" key binding --- .../Input/Bindings/GlobalActionContainer.cs | 4 ++++ .../GlobalActionKeyBindingStrings.cs | 5 ++++ osu.Game/Screens/Edit/Editor.cs | 23 ++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 4a233e5bf3..476fda81b7 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -90,6 +90,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F3 }, GlobalAction.EditorTimingMode), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.EditorSetupMode), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode), + new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.EditorDuplicateSelection), new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft), new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight), new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode), @@ -346,5 +347,8 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleProfile))] ToggleProfile, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDuplicateSelection))] + EditorDuplicateSelection } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 172818c1c0..403fa8017a 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -184,6 +184,11 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorTapForBPM => new TranslatableString(getKey(@"editor_tap_for_bpm"), @"Tap for BPM"); + /// + /// "Duplicate selection" + /// + public static LocalisableString EditorDuplicateSelection => new TranslatableString(getKey(@"editor_duplicate_selection"), @"Duplicate selection"); + /// /// "Cycle grid display mode" /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3dfc7010f3..51f2ca16c2 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -304,6 +304,7 @@ namespace osu.Game.Screens.Edit cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), + duplicateMenuItem = new EditorMenuItem("Duplicate", MenuItemType.Standard, Duplicate), } }, new MenuItem("View") @@ -575,6 +576,10 @@ namespace osu.Game.Screens.Edit this.Exit(); return true; + case GlobalAction.EditorDuplicateSelection: + Duplicate(); + return true; + case GlobalAction.EditorComposeMode: Mode.Value = EditorScreenMode.Compose; return true; @@ -741,6 +746,7 @@ namespace osu.Game.Screens.Edit private EditorMenuItem cutMenuItem; private EditorMenuItem copyMenuItem; + private EditorMenuItem duplicateMenuItem; private EditorMenuItem pasteMenuItem; private readonly BindableWithCurrent canCut = new BindableWithCurrent(); @@ -750,7 +756,11 @@ namespace osu.Game.Screens.Edit private void setUpClipboardActionAvailability() { canCut.Current.BindValueChanged(cut => cutMenuItem.Action.Disabled = !cut.NewValue, true); - canCopy.Current.BindValueChanged(copy => copyMenuItem.Action.Disabled = !copy.NewValue, true); + canCopy.Current.BindValueChanged(copy => + { + copyMenuItem.Action.Disabled = !copy.NewValue; + duplicateMenuItem.Action.Disabled = !copy.NewValue; + }, true); canPaste.Current.BindValueChanged(paste => pasteMenuItem.Action.Disabled = !paste.NewValue, true); } @@ -765,6 +775,17 @@ namespace osu.Game.Screens.Edit protected void Copy() => currentScreen?.Copy(); + protected void Duplicate() + { + // This is an initial implementation just to get an idea of how people used this function. + // There are a couple of differences from osu!stable's implementation which will require more work to match: + // - The "clipboard" is not populated during the duplication process. + // - The duplicated hitobjects are inserted after the original pattern (add one beat_length and then quantize using beat snap). + // - The duplicated hitobjects are selected (but this is also applied for all paste operations so should be changed there). + Copy(); + Paste(); + } + protected void Paste() => currentScreen?.Paste(); #endregion From d0e6bda9ef52dbf0a53d4c9685cc8c7c35535ad5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 15:19:36 +0900 Subject: [PATCH 186/321] Stop `HitObjectComposer` from handling `Shift`+`Number` keys --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3bed835854..520fcb0290 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Edit protected override bool OnKeyDown(KeyDownEvent e) { - if (e.ControlPressed || e.AltPressed || e.SuperPressed) + if (e.ControlPressed || e.AltPressed || e.SuperPressed || e.ShiftPressed) return false; if (checkLeftToggleFromKey(e.Key, out int leftIndex)) From 1d5df150272056da2780dd6ab06ad8923da5e5ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 15:20:15 +0900 Subject: [PATCH 187/321] Add ability to use `Shift`+`Number` to set current beat divisor in editor --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 20 ++++++++++++++++ .../Compose/Components/BeatDivisorControl.cs | 23 ++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index a7faf961cf..aa8e202e22 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -25,6 +25,26 @@ namespace osu.Game.Screens.Edit BindValueChanged(_ => ensureValidDivisor()); } + /// + /// Set a divisor, updating the valid divisor range appropriately. + /// + /// The intended divisor. + public void SetArbitraryDivisor(int divisor) + { + // If the current valid divisor range doesn't contain the proposed value, attempt to find one which does. + if (!ValidDivisors.Value.Presets.Contains(divisor)) + { + if (BeatDivisorPresetCollection.COMMON.Presets.Contains(divisor)) + ValidDivisors.Value = BeatDivisorPresetCollection.COMMON; + else if (BeatDivisorPresetCollection.TRIPLETS.Presets.Contains(divisor)) + ValidDivisors.Value = BeatDivisorPresetCollection.TRIPLETS; + else + ValidDivisors.Value = BeatDivisorPresetCollection.Custom(divisor); + } + + Value = divisor; + } + private void updateBindableProperties() { ensureValidDivisor(); diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 19ea2162a3..6dca799549 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -209,6 +209,17 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.ShiftPressed && e.Key >= Key.Number1 && e.Key <= Key.Number9) + { + beatDivisor.SetArbitraryDivisor(e.Key - Key.Number0); + return true; + } + + return base.OnKeyDown(e); + } + internal class DivisorDisplay : OsuAnimatedButton, IHasPopover { public BindableBeatDivisor BeatDivisor { get; } = new BindableBeatDivisor(); @@ -306,17 +317,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return; } - if (!BeatDivisor.ValidDivisors.Value.Presets.Contains(divisor)) - { - if (BeatDivisorPresetCollection.COMMON.Presets.Contains(divisor)) - BeatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.COMMON; - else if (BeatDivisorPresetCollection.TRIPLETS.Presets.Contains(divisor)) - BeatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.TRIPLETS; - else - BeatDivisor.ValidDivisors.Value = BeatDivisorPresetCollection.Custom(divisor); - } - - BeatDivisor.Value = divisor; + BeatDivisor.SetArbitraryDivisor(divisor); this.HidePopover(); } From f08270f6b06945f6f86655d36861ed7bf54aaef5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 15:54:09 +0900 Subject: [PATCH 188/321] Fix incorrect `maxBaseScore` accounting due to silly oversight --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index ec98d00e68..f8b5085a70 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.Gameplay void applyHitV2(int baseScore) { - maxBaseScore += baseScore; + maxBaseScore += base_great; currentBaseScore += baseScore; comboPortion += baseScore * (1 + ++currentCombo / 10.0); From 14704fd07cdaa6fe51482ceb56a29f7ae33c5a2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 16:08:49 +0900 Subject: [PATCH 189/321] Fix crash when exiting seeding editor too soon Closes https://github.com/ppy/osu/issues/20783. --- .../Screens/Editors/SeedingEditorScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index 1bc929604d..348661e2a3 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -239,17 +239,17 @@ namespace osu.Game.Tournament.Screens.Editors var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = Model.ID }); - req.Success += res => + req.Success += res => Schedule(() => { Model.Beatmap = new TournamentBeatmap(res); updatePanel(); - }; + }); - req.Failure += _ => + req.Failure += _ => Schedule(() => { Model.Beatmap = null; updatePanel(); - }; + }); API.Queue(req); }, true); From 9def12c60bceb6c75a92f74be14aafd1d10b8fe6 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 24 Oct 2022 22:50:56 +0300 Subject: [PATCH 190/321] Write new tests --- .../SongSelect/TestSceneBeatmapCarousel.cs | 103 ++++++++++-------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 0e72463d1e..303af61d20 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -827,6 +827,63 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); } + [Test] + public void TestCarouselSelectsNextWhenPreviousIsFiltered() + { + List sets = new List(); + + // 10 sets that go osu! -> taiko -> catch -> osu! -> ... + for (int i = 0; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); + sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); + } + + // Sort mode is important to keep the ruleset order + loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); + setSelected(1, 1); + + for (int i = 1; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); + AddStep($"Set ruleset to {rulesetInfo.ShortName}", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesetInfo, Sort = SortMode.Title }, false); + }); + waitForSelection(i + 1, 1); + } + } + + [Test] + public void TestCarouselSelectsBackwardsWhenDistanceIsShorter() + { + List sets = new List(); + + // 10 sets that go taiko, osu!, osu!, osu!, taiko, osu!, osu!, osu!, ... + for (int i = 0; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 4 == 0 ? 1 : 0); + sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); + } + + // Sort mode is important to keep the ruleset order + loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); + + for (int i = 2; i < 10; i += 4) + { + setSelected(i, 1); + AddStep("Set ruleset to taiko", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1), Sort = SortMode.Title }, false); + }); + waitForSelection(i - 1, 1); + AddStep("Remove ruleset filter", () => + { + carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false); + }); + } + } + [Test] public void TestCarouselRemembersSelection() { @@ -862,52 +919,6 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1); } - [Test] - public void TestRandomFallbackOnNonMatchingPrevious() - { - List manySets = new List(); - - AddStep("populate maps", () => - { - manySets.Clear(); - - for (int i = 0; i < 10; i++) - { - manySets.Add(TestResources.CreateTestBeatmapSetInfo(3, new[] - { - // all taiko except for first - rulesets.GetRuleset(i > 0 ? 1 : 0) - })); - } - }); - - loadBeatmaps(manySets); - - for (int i = 0; i < 10; i++) - { - AddStep("Reset filter", () => carousel.Filter(new FilterCriteria(), false)); - - AddStep("select first beatmap", () => carousel.SelectBeatmap(manySets.First().Beatmaps.First())); - - AddStep("Toggle non-matching filter", () => - { - carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false); - }); - - AddAssert("selection lost", () => carousel.SelectedBeatmapInfo == null); - - AddStep("Restore different ruleset filter", () => - { - carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false); - eagerSelectedIDs.Add(carousel.SelectedBeatmapSet!.ID); - }); - - AddAssert("selection changed", () => !carousel.SelectedBeatmapInfo!.Equals(manySets.First().Beatmaps.First())); - } - - AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2); - } - [Test] public void TestFilteringByUserStarDifficulty() { From b95091fbb0a79bbb4d42a173c30354aabfb864a0 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Mon, 24 Oct 2022 22:55:09 +0300 Subject: [PATCH 191/321] Move test to improve diff readability --- .../SongSelect/TestSceneBeatmapCarousel.cs | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 303af61d20..eacaf7f92e 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -827,63 +827,6 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); } - [Test] - public void TestCarouselSelectsNextWhenPreviousIsFiltered() - { - List sets = new List(); - - // 10 sets that go osu! -> taiko -> catch -> osu! -> ... - for (int i = 0; i < 10; i++) - { - var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); - sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); - } - - // Sort mode is important to keep the ruleset order - loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); - setSelected(1, 1); - - for (int i = 1; i < 10; i++) - { - var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); - AddStep($"Set ruleset to {rulesetInfo.ShortName}", () => - { - carousel.Filter(new FilterCriteria { Ruleset = rulesetInfo, Sort = SortMode.Title }, false); - }); - waitForSelection(i + 1, 1); - } - } - - [Test] - public void TestCarouselSelectsBackwardsWhenDistanceIsShorter() - { - List sets = new List(); - - // 10 sets that go taiko, osu!, osu!, osu!, taiko, osu!, osu!, osu!, ... - for (int i = 0; i < 10; i++) - { - var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 4 == 0 ? 1 : 0); - sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); - } - - // Sort mode is important to keep the ruleset order - loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); - - for (int i = 2; i < 10; i += 4) - { - setSelected(i, 1); - AddStep("Set ruleset to taiko", () => - { - carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1), Sort = SortMode.Title }, false); - }); - waitForSelection(i - 1, 1); - AddStep("Remove ruleset filter", () => - { - carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false); - }); - } - } - [Test] public void TestCarouselRemembersSelection() { @@ -966,6 +909,63 @@ namespace osu.Game.Tests.Visual.SongSelect checkVisibleItemCount(true, 15); } + [Test] + public void TestCarouselSelectsNextWhenPreviousIsFiltered() + { + List sets = new List(); + + // 10 sets that go osu! -> taiko -> catch -> osu! -> ... + for (int i = 0; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); + sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); + } + + // Sort mode is important to keep the ruleset order + loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); + setSelected(1, 1); + + for (int i = 1; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 3); + AddStep($"Set ruleset to {rulesetInfo.ShortName}", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesetInfo, Sort = SortMode.Title }, false); + }); + waitForSelection(i + 1, 1); + } + } + + [Test] + public void TestCarouselSelectsBackwardsWhenDistanceIsShorter() + { + List sets = new List(); + + // 10 sets that go taiko, osu!, osu!, osu!, taiko, osu!, osu!, osu!, ... + for (int i = 0; i < 10; i++) + { + var rulesetInfo = rulesets.AvailableRulesets.ElementAt(i % 4 == 0 ? 1 : 0); + sets.Add(TestResources.CreateTestBeatmapSetInfo(5, new[] { rulesetInfo })); + } + + // Sort mode is important to keep the ruleset order + loadBeatmaps(sets, () => new FilterCriteria { Sort = SortMode.Title }); + + for (int i = 2; i < 10; i += 4) + { + setSelected(i, 1); + AddStep("Set ruleset to taiko", () => + { + carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1), Sort = SortMode.Title }, false); + }); + waitForSelection(i - 1, 1); + AddStep("Remove ruleset filter", () => + { + carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false); + }); + } + } + private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null, Action carouselAdjust = null, int? count = null, bool randomDifficulties = false) { From da74690ec9363c3be62d5a9a5e5c76b8f0f63033 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 11:38:58 +0900 Subject: [PATCH 192/321] Add test coverage of clone operations --- .../Editing/TestSceneEditorClipboard.cs | 31 +++++++++++++++++++ osu.Game/Tests/Visual/EditorTestScene.cs | 2 ++ 2 files changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs index 23e137865c..9fa3b2db1a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs @@ -155,6 +155,20 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("composer selection box is visible", () => Editor.ChildrenOfType().First().ChildrenOfType().First().Alpha > 0); } + [Test] + public void TestClone() + { + var addedObject = new HitCircle { StartTime = 1000 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); + + AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1); + AddStep("clone", () => Editor.Duplicate()); + AddAssert("is two objects", () => EditorBeatmap.HitObjects.Count == 2); + AddStep("clone", () => Editor.Duplicate()); + AddAssert("is three objects", () => EditorBeatmap.HitObjects.Count == 3); + } + [Test] public void TestCutNothing() { @@ -175,5 +189,22 @@ namespace osu.Game.Tests.Visual.Editing AddStep("paste hitobject", () => Editor.Paste()); AddAssert("are no objects", () => EditorBeatmap.HitObjects.Count == 0); } + + [Test] + public void TestCloneNothing() + { + // Add arbitrary object and copy to clipboard. + // This is tested to ensure that clone doesn't incorrectly read from the clipboard when no selection is made. + var addedObject = new HitCircle { StartTime = 1000 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); + AddStep("copy hitobject", () => Editor.Copy()); + + AddStep("deselect all objects", () => EditorBeatmap.SelectedHitObjects.Clear()); + + AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1); + AddStep("clone", () => Editor.Duplicate()); + AddAssert("still one object", () => EditorBeatmap.HitObjects.Count == 1); + } } } diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index ced72aa593..0e24c5c47a 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -110,6 +110,8 @@ namespace osu.Game.Tests.Visual public new void Paste() => base.Paste(); + public new void Duplicate() => base.Duplicate(); + public new void SwitchToDifficulty(BeatmapInfo beatmapInfo) => base.SwitchToDifficulty(beatmapInfo); public new void CreateNewDifficulty(RulesetInfo rulesetInfo) => base.CreateNewDifficulty(rulesetInfo); From 1e579e06f8079e82f0694db8af6faa8ade586a08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 11:42:12 +0900 Subject: [PATCH 193/321] Fix duplicate working incorrectly if there is no selection currently made --- osu.Game/Screens/Edit/Editor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 51f2ca16c2..cc551c3262 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -777,6 +777,10 @@ namespace osu.Game.Screens.Edit protected void Duplicate() { + // Avoid attempting to clone if copying is not available (as it may result in pasting something unexpected). + if (!canCopy.Value) + return; + // This is an initial implementation just to get an idea of how people used this function. // There are a couple of differences from osu!stable's implementation which will require more work to match: // - The "clipboard" is not populated during the duplication process. From 4d4f6e25bab755fe8de9d90909a3beec2a22590c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 11:43:23 +0900 Subject: [PATCH 194/321] Rename to "clone" instead of "duplicate" --- osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs | 6 +++--- osu.Game/Input/Bindings/GlobalActionContainer.cs | 6 +++--- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 4 ++-- osu.Game/Screens/Edit/Editor.cs | 8 ++++---- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs index 9fa3b2db1a..0a5a1febe4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs @@ -163,9 +163,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1); - AddStep("clone", () => Editor.Duplicate()); + AddStep("clone", () => Editor.Clone()); AddAssert("is two objects", () => EditorBeatmap.HitObjects.Count == 2); - AddStep("clone", () => Editor.Duplicate()); + AddStep("clone", () => Editor.Clone()); AddAssert("is three objects", () => EditorBeatmap.HitObjects.Count == 3); } @@ -203,7 +203,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("deselect all objects", () => EditorBeatmap.SelectedHitObjects.Clear()); AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1); - AddStep("clone", () => Editor.Duplicate()); + AddStep("clone", () => Editor.Clone()); AddAssert("still one object", () => EditorBeatmap.HitObjects.Count == 1); } } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 476fda81b7..a58c6723ef 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F3 }, GlobalAction.EditorTimingMode), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.EditorSetupMode), new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode), - new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.EditorDuplicateSelection), + new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.EditorCloneSelection), new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft), new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight), new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode), @@ -348,7 +348,7 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleProfile))] ToggleProfile, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDuplicateSelection))] - EditorDuplicateSelection + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCloneSelection))] + EditorCloneSelection } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 403fa8017a..14e0bbbced 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -185,9 +185,9 @@ namespace osu.Game.Localisation public static LocalisableString EditorTapForBPM => new TranslatableString(getKey(@"editor_tap_for_bpm"), @"Tap for BPM"); /// - /// "Duplicate selection" + /// "Clone selection" /// - public static LocalisableString EditorDuplicateSelection => new TranslatableString(getKey(@"editor_duplicate_selection"), @"Duplicate selection"); + public static LocalisableString EditorCloneSelection => new TranslatableString(getKey(@"editor_clone_selection"), @"Clone selection"); /// /// "Cycle grid display mode" diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index cc551c3262..c2899bc1ec 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -304,7 +304,7 @@ namespace osu.Game.Screens.Edit cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), - duplicateMenuItem = new EditorMenuItem("Duplicate", MenuItemType.Standard, Duplicate), + duplicateMenuItem = new EditorMenuItem("Clone", MenuItemType.Standard, Clone), } }, new MenuItem("View") @@ -576,8 +576,8 @@ namespace osu.Game.Screens.Edit this.Exit(); return true; - case GlobalAction.EditorDuplicateSelection: - Duplicate(); + case GlobalAction.EditorCloneSelection: + Clone(); return true; case GlobalAction.EditorComposeMode: @@ -775,7 +775,7 @@ namespace osu.Game.Screens.Edit protected void Copy() => currentScreen?.Copy(); - protected void Duplicate() + protected void Clone() { // Avoid attempting to clone if copying is not available (as it may result in pasting something unexpected). if (!canCopy.Value) diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 0e24c5c47a..0e7bb72162 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual public new void Paste() => base.Paste(); - public new void Duplicate() => base.Duplicate(); + public new void Clone() => base.Clone(); public new void SwitchToDifficulty(BeatmapInfo beatmapInfo) => base.SwitchToDifficulty(beatmapInfo); From 1b42f5ec6ec79ce7d73dcbd917fd33f5a518a21a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 11:51:31 +0900 Subject: [PATCH 195/321] Add test coverage of `BeatDivisorControl` key bindings --- .../Editing/TestSceneBeatDivisorControl.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index ecd7732862..f2d27b9117 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -106,6 +106,49 @@ namespace osu.Game.Tests.Visual.Editing assertBeatSnap(16); } + [Test] + public void TestKeyboardNavigation() + { + pressKey(1); + assertBeatSnap(1); + assertPreset(BeatDivisorType.Common); + + pressKey(2); + assertBeatSnap(2); + assertPreset(BeatDivisorType.Common); + + pressKey(3); + assertBeatSnap(3); + assertPreset(BeatDivisorType.Triplets); + + pressKey(4); + assertBeatSnap(4); + assertPreset(BeatDivisorType.Common); + + pressKey(5); + assertBeatSnap(5); + assertPreset(BeatDivisorType.Custom, 5); + + pressKey(6); + assertBeatSnap(6); + assertPreset(BeatDivisorType.Triplets); + + pressKey(7); + assertBeatSnap(7); + assertPreset(BeatDivisorType.Custom, 7); + + pressKey(8); + assertBeatSnap(8); + assertPreset(BeatDivisorType.Common); + + void pressKey(int key) => AddStep($"press shift+{key}", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.Number0 + key); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + } + [Test] public void TestBeatPresetNavigation() { From 8e4f5381e3cba926282ea72d52621cca6be6a52e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 12:03:46 +0900 Subject: [PATCH 196/321] Add top level test coverage of editor shortcuts --- .../Visual/Editing/TestSceneEditorBindings.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneEditorBindings.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBindings.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBindings.cs new file mode 100644 index 0000000000..5771d64775 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBindings.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + /// + /// Test editor hotkeys at a high level to ensure they all work well together. + /// + public class TestSceneEditorBindings : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + [Test] + public void TestBeatDivisorChangeHotkeys() + { + AddStep("hold shift", () => InputManager.PressKey(Key.LShift)); + + AddStep("press 4", () => InputManager.Key(Key.Number4)); + AddAssert("snap updated to 4", () => EditorBeatmap.BeatmapInfo.BeatDivisor, () => Is.EqualTo(4)); + + AddStep("press 6", () => InputManager.Key(Key.Number6)); + AddAssert("snap updated to 6", () => EditorBeatmap.BeatmapInfo.BeatDivisor, () => Is.EqualTo(6)); + + AddStep("release shift", () => InputManager.ReleaseKey(Key.LShift)); + } + } +} From 2456a18bc1be49d23b3beb0e1ca2fed087f148d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 12:48:17 +0900 Subject: [PATCH 197/321] Rewrite implementation to avoid duplicated LINQ returns --- .../Carousel/CarouselGroupEagerSelect.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 2c1ccebfab..a96d768ff9 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -115,26 +115,24 @@ namespace osu.Game.Screens.Select.Carousel protected virtual CarouselItem GetNextToSelect() { int forwardsIndex = lastSelectedIndex; + bool hasForwards; + int backwardsIndex = lastSelectedIndex; + bool hasBackwards; - while (true) + while ((hasBackwards = backwardsIndex >= 0) | (hasForwards = forwardsIndex < Items.Count)) { - // check if a direction has been exhausted and an item (or null) from the other direction should be returned - if (forwardsIndex >= Items.Count) - return Items.Reverse().Skip(Items.Count - backwardsIndex - 1).FirstOrDefault(item => !item.Filtered.Value); - if (backwardsIndex < 0) - return Items.Skip(forwardsIndex).FirstOrDefault(item => !item.Filtered.Value); - - // check if an unfiltered item has been reached - if (!Items[forwardsIndex].Filtered.Value) + if (hasForwards && !Items[forwardsIndex].Filtered.Value) return Items[forwardsIndex]; - if (!Items[backwardsIndex].Filtered.Value) + + if (hasBackwards && !Items[backwardsIndex].Filtered.Value) return Items[backwardsIndex]; - // increment the indices forwardsIndex++; backwardsIndex--; } + + return null; } protected virtual void PerformSelection() From 3d72ff28c3e09b1e1be93917ceaf2957e34d1adc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 13:25:38 +0900 Subject: [PATCH 198/321] Add test scene for "Freeze Frame" mod --- .../Mods/TestSceneOsuModFreezeFrame.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.cs new file mode 100644 index 0000000000..7d7b2d9071 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFreezeFrame.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 NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModFreezeFrame : OsuModTestScene + { + [Test] + public void TestFreezeFrame() + { + CreateModTest(new ModTestData + { + Mod = new OsuModFreezeFrame(), + PassCondition = () => true, + Autoplay = false, + }); + } + } +} From 588fc750cdeac7b2b915506f62eb168adb960687 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 13:26:32 +0900 Subject: [PATCH 199/321] Change approach circles to all appear at the same time, but don't adjust AR --- .../Mods/OsuModFreezeFrame.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 8bf2c89057..2187d33a49 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -29,12 +29,17 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; //mod breaks normal approach circle preempt - private double approachCircleTimePreempt; + private double originalPreempt; public void ApplyToBeatmap(IBeatmap beatmap) { + var firstHitObject = beatmap.HitObjects.OfType().FirstOrDefault(); + if (firstHitObject == null) + return; + double lastNewComboTime = 0; - approachCircleTimePreempt = beatmap.HitObjects.OfType().FirstOrDefault()!.TimePreempt; + + originalPreempt = firstHitObject.TimePreempt; foreach (var obj in beatmap.HitObjects.OfType()) { @@ -72,17 +77,12 @@ namespace osu.Game.Rulesets.Osu.Mods var hitCircle = drawableHitCircle.HitObject; var approachCircle = drawableHitCircle.ApproachCircle; - approachCircle.ClearTransforms(); - approachCircle.ScaleTo(4); - approachCircle.FadeTo(0); + // Reapply scale, ensuring the AR isn't changes due to the new preempt. + approachCircle.ClearTransforms(targetMember: nameof(approachCircle.Scale)); + approachCircle.ScaleTo(4 * (float)(hitCircle.TimePreempt / originalPreempt)); - using (drawableHitCircle.ApproachCircle.BeginAbsoluteSequence(hitCircle.StartTime - approachCircleTimePreempt)) - { - //Redo ApproachCircle animation with correct startTime. - approachCircle.LifetimeStart = hitCircle.StartTime - approachCircleTimePreempt; - approachCircle.FadeTo(1, Math.Min(hitCircle.TimeFadeIn * 2, hitCircle.TimePreempt)); - approachCircle.ScaleTo(1, approachCircleTimePreempt).Then().Expire(); - } + using (drawableHitCircle.ApproachCircle.BeginAbsoluteSequence(hitCircle.StartTime - hitCircle.TimePreempt)) + approachCircle.ScaleTo(1, hitCircle.TimePreempt).Then().Expire(); }; } } From 279ef556e3c4eb818d40d837809739cf7a8eb8ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 13:27:26 +0900 Subject: [PATCH 200/321] Fix typon in comment --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 2187d33a49..20d75ab019 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Mods var hitCircle = drawableHitCircle.HitObject; var approachCircle = drawableHitCircle.ApproachCircle; - // Reapply scale, ensuring the AR isn't changes due to the new preempt. + // Reapply scale, ensuring the AR isn't changed due to the new preempt. approachCircle.ClearTransforms(targetMember: nameof(approachCircle.Scale)); approachCircle.ScaleTo(4 * (float)(hitCircle.TimePreempt / originalPreempt)); From 22ea9a8ab4f2d4c07820935f3c3dc823c8c1b65b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 13:34:21 +0900 Subject: [PATCH 201/321] Add early return of carousel group has no items --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index a96d768ff9..6a53f8f527 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -114,6 +114,9 @@ namespace osu.Game.Screens.Select.Carousel /// An unfiltered item nearest to the last selected one or null if all items are filtered protected virtual CarouselItem GetNextToSelect() { + if (Items.Count == 0) + return null; + int forwardsIndex = lastSelectedIndex; bool hasForwards; From 02a3f8c17fb14bdd2c699c20a1416d0563aec045 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 14:09:22 +0900 Subject: [PATCH 202/321] Allow both distance snap and grid snap to be applied at the same time --- .../Edit/OsuHitObjectComposer.cs | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6b4a6e39d9..67061bdaf2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -80,19 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit placementObject = EditorBeatmap.PlacementObject.GetBoundCopy(); placementObject.ValueChanged += _ => updateDistanceSnapGrid(); - distanceSnapToggle.ValueChanged += _ => - { - updateDistanceSnapGrid(); - - if (distanceSnapToggle.Value == TernaryState.True) - rectangularGridSnapToggle.Value = TernaryState.False; - }; - - rectangularGridSnapToggle.ValueChanged += _ => - { - if (rectangularGridSnapToggle.Value == TernaryState.True) - distanceSnapToggle.Value = TernaryState.False; - }; + distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid(); // we may be entering the screen with a selection already active updateDistanceSnapGrid(); @@ -134,22 +122,27 @@ namespace osu.Game.Rulesets.Osu.Edit if (snapType.HasFlagFast(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) return snapResult; + SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); + if (snapType.HasFlagFast(SnapType.Grids)) { if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) { (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); - return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + + result.ScreenSpacePosition = distanceSnapGrid.ToScreenSpace(pos); + result.Time = time; } if (rectangularGridSnapToggle.Value == TernaryState.True) { - Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(screenSpacePosition)); - return new SnapResult(rectangularPositionSnapGrid.ToScreenSpace(pos), null, PlayfieldAtScreenSpacePosition(screenSpacePosition)); + Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(result.ScreenSpacePosition)); + + result.ScreenSpacePosition = rectangularPositionSnapGrid.ToScreenSpace(pos); } } - return base.FindSnappedPositionAndTime(screenSpacePosition, snapType); + return result; } private bool snapToVisibleBlueprints(Vector2 screenSpacePosition, out SnapResult snapResult) From 2f0283e4d4b6d547a2c7a04387a514a115142988 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 14:22:28 +0900 Subject: [PATCH 203/321] Simplify logic with new multi-grid snap support --- .../Edit/CatchHitObjectComposer.cs | 21 +++------- .../Edit/OsuHitObjectComposer.cs | 42 ++++++------------- 2 files changed, 18 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 27235bd62e..9ba17c89ff 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -103,25 +103,16 @@ namespace osu.Game.Rulesets.Catch.Edit base.OnKeyUp(e); } - private TernaryState? distanceSnapBeforeMomentary; + private bool distanceSnapMomentary; private void handleToggleViaKey(KeyboardEvent key) { - if (key.AltPressed) + bool altPressed = key.AltPressed; + + if (altPressed != distanceSnapMomentary) { - if (distanceSnapBeforeMomentary == null) - { - distanceSnapBeforeMomentary = distanceSnapToggle.Value; - distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - } - } - else - { - if (distanceSnapBeforeMomentary != null) - { - distanceSnapToggle.Value = distanceSnapBeforeMomentary.Value; - distanceSnapBeforeMomentary = null; - } + distanceSnapMomentary = altPressed; + distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 838f18bad5..bf7cddd099 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -247,43 +247,25 @@ namespace osu.Game.Rulesets.Osu.Edit return base.AdjustDistanceSpacing(action, amount); } - private TernaryState? gridSnapBeforeMomentary; - private TernaryState? distanceSnapBeforeMomentary; + private bool distanceSnapMomentary; + private bool gridSnapMomentary; private void handleToggleViaKey(KeyboardEvent key) { - if (key.ShiftPressed) + bool altPressed = key.AltPressed; + + if (altPressed != distanceSnapMomentary) { - if (distanceSnapBeforeMomentary == null && gridSnapBeforeMomentary == null) - { - gridSnapBeforeMomentary = rectangularGridSnapToggle.Value; - rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - } - } - else - { - if (gridSnapBeforeMomentary != null) - { - rectangularGridSnapToggle.Value = gridSnapBeforeMomentary.Value; - gridSnapBeforeMomentary = null; - } + distanceSnapMomentary = altPressed; + distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; } - if (key.AltPressed) + bool shiftPressed = key.ShiftPressed; + + if (shiftPressed != gridSnapMomentary) { - if (gridSnapBeforeMomentary == null && distanceSnapBeforeMomentary == null) - { - distanceSnapBeforeMomentary = distanceSnapToggle.Value; - distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - } - } - else - { - if (distanceSnapBeforeMomentary != null) - { - distanceSnapToggle.Value = distanceSnapBeforeMomentary.Value; - distanceSnapBeforeMomentary = null; - } + gridSnapMomentary = shiftPressed; + rectangularGridSnapToggle.Value = rectangularGridSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; } } From a9755295e44445e25c5aeebec212f5baa83b89df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 14:32:08 +0900 Subject: [PATCH 204/321] Update tests in line with new behaviour --- .../Editor/TestSceneOsuEditorGrids.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 1e73885540..479687c496 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -20,20 +20,27 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); [Test] - public void TestGridExclusivity() + public void TestGridToggles() { AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); rectangularGridActive(false); AddStep("enable rectangular grid", () => InputManager.Key(Key.Y)); - AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + AddUntilStep("distance snap grid still visible", () => this.ChildrenOfType().Any()); rectangularGridActive(true); - AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + AddStep("disable distance snap grid", () => InputManager.Key(Key.T)); + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); - AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + rectangularGridActive(true); + + AddStep("disable rectangular grid", () => InputManager.Key(Key.Y)); + AddUntilStep("distance snap grid still hidden", () => !this.ChildrenOfType().Any()); rectangularGridActive(false); } From 16ef0b09e8a86eb33b0bce3ca2a230ab24c840bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 14:36:35 +0900 Subject: [PATCH 205/321] Add test coverage of momentary toggles --- .../Editor/TestSceneOsuEditorGrids.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index 479687c496..f6d3512fe6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -44,6 +44,28 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor rectangularGridActive(false); } + [Test] + public void TestDistanceSnapMomentaryToggle() + { + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft)); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + } + + [Test] + public void TestGridSnapMomentaryToggle() + { + rectangularGridActive(false); + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); + rectangularGridActive(true); + AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); + rectangularGridActive(false); + } + private void rectangularGridActive(bool active) { AddStep("choose placement tool", () => InputManager.Key(Key.Number2)); From da93849b8084b7bc0fb057704ee1a7a1606bf82f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 14:55:33 +0900 Subject: [PATCH 206/321] Rename some pieces and better document `SampleBankInfo` --- .../Objects/Legacy/ConvertHitObjectParser.cs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index b289299a63..930ee0448f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -199,8 +199,8 @@ namespace osu.Game.Rulesets.Objects.Legacy if (stringAddBank == @"none") stringAddBank = null; - bankInfo.Normal = stringBank; - bankInfo.Add = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank; + bankInfo.BankForNormal = stringBank; + bankInfo.BankForAdditions = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank; if (split.Length > 2) bankInfo.CustomSampleBank = Parsing.ParseInt(split[2]); @@ -447,32 +447,54 @@ namespace osu.Game.Rulesets.Objects.Legacy var soundTypes = new List { - new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.Normal, bankInfo.Volume, bankInfo.CustomSampleBank, + new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank, // if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample. // None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal)) }; if (type.HasFlagFast(LegacyHitSoundType.Finish)) - soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank)); + soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); if (type.HasFlagFast(LegacyHitSoundType.Whistle)) - soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank)); + soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); if (type.HasFlagFast(LegacyHitSoundType.Clap)) - soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank)); + soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); return soundTypes; } private class SampleBankInfo { + /// + /// An optional overriding filename which causes all bank/sample specifications to be ignored. + /// public string Filename; - public string Normal; - public string Add; + /// + /// The bank identifier to use for the base ("hitnormal") sample. + /// Transferred to when appropriate. + /// + public string BankForNormal; + + /// + /// The bank identifier to use for additions ("hitwhistle", "hitfinish", "hitclap"). + /// Transferred to when appropriate. + /// + public string BankForAdditions; + + /// + /// Hit sample volume (0-100). + /// See . + /// public int Volume; + /// + /// The index of the custom sample bank. Is only used if 2 or above for "reasons". + /// This will add a suffix to lookups, allowing extended bank lookups (ie. "normal-hitnormal-2"). + /// See . + /// public int CustomSampleBank; public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone(); @@ -503,7 +525,8 @@ namespace osu.Game.Rulesets.Objects.Legacy public sealed override HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) => With(newName, newBank, newVolume); - public virtual LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, Optional newCustomSampleBank = default, + public virtual LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, + Optional newCustomSampleBank = default, Optional newIsLayered = default) => new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered)); @@ -537,7 +560,8 @@ namespace osu.Game.Rulesets.Objects.Legacy Path.ChangeExtension(Filename, null) }; - public sealed override LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, Optional newCustomSampleBank = default, + public sealed override LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, + Optional newCustomSampleBank = default, Optional newIsLayered = default) => new FileHitSampleInfo(Filename, newVolume.GetOr(Volume)); From f4aae9138bf98ec4475ce14d709dae20be4ca001 Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Tue, 25 Oct 2022 08:48:45 +0200 Subject: [PATCH 207/321] Remove Incompatibility with HD --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index 20d75ab019..bea5d4f5d9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Burn the notes into your memory."; //Alters the transforms of the approach circles, breaking the effects of these mods. - public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent), typeof(OsuModHidden) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModApproachDifferent) }; public override ModType Type => ModType.Fun; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 2d7c0c82de..996ee1cddb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModFreezeFrame) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) }; public const double FADE_IN_DURATION_MULTIPLIER = 0.4; public const double FADE_OUT_DURATION_MULTIPLIER = 0.3; From cf3bf41b4902f754d64344bda326928190dd3ff7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Oct 2022 18:57:15 +0900 Subject: [PATCH 208/321] Harden and simplify logic to avoid weird issues --- .../Select/Carousel/CarouselGroupEagerSelect.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 6a53f8f527..34865c7f51 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -118,13 +118,16 @@ namespace osu.Game.Screens.Select.Carousel return null; int forwardsIndex = lastSelectedIndex; - bool hasForwards; - int backwardsIndex = lastSelectedIndex; - bool hasBackwards; - while ((hasBackwards = backwardsIndex >= 0) | (hasForwards = forwardsIndex < Items.Count)) + while (true) { + bool hasBackwards = backwardsIndex >= 0 && backwardsIndex < Items.Count; + bool hasForwards = forwardsIndex < Items.Count; + + if (!hasBackwards && !hasForwards) + return null; + if (hasForwards && !Items[forwardsIndex].Filtered.Value) return Items[forwardsIndex]; @@ -134,8 +137,6 @@ namespace osu.Game.Screens.Select.Carousel forwardsIndex++; backwardsIndex--; } - - return null; } protected virtual void PerformSelection() From 92ab39f5c2d94c34964b7c22894f3914c7aa8311 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 25 Oct 2022 19:16:23 +0900 Subject: [PATCH 209/321] Fix hold note disappear instantly --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 48647f9f5f..14dbc432ff 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -69,6 +69,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// private double? releaseTime; + public override double MaximumJudgementOffset => Tail.MaximumJudgementOffset; + public DrawableHoldNote() : this(null) { @@ -260,7 +262,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables tick.MissForcefully(); } - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(r => r.Type = Tail.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); endHold(); } From 0b8b3223222d4cfe3311e65707bd5fe3d59ad7d5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 25 Oct 2022 19:48:33 +0900 Subject: [PATCH 210/321] Fix tests --- osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs | 2 +- osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 8f776ff507..0296303867 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Tests assertHeadJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.Miss); - assertNoteJudgement(HitResult.IgnoreHit); + assertNoteJudgement(HitResult.IgnoreMiss); } /// diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index 1f139b5b78..464dbecee5 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -76,8 +76,8 @@ namespace osu.Game.Rulesets.Mania.Tests performTest(objects, new List()); - addJudgementAssert(objects[0], HitResult.IgnoreHit); - addJudgementAssert(objects[1], HitResult.IgnoreHit); + addJudgementAssert(objects[0], HitResult.IgnoreMiss); + addJudgementAssert(objects[1], HitResult.IgnoreMiss); } [Test] From 242f80b8b31a670032048d305469302aa7afd123 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 25 Oct 2022 15:49:14 +0200 Subject: [PATCH 211/321] Set scrollspeed directly at beatmap decode --- .../TestSceneControlPointConversion.cs | 38 ------------------- .../Beatmaps/TaikoBeatmapConverter.cs | 23 ----------- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 7 +--- 3 files changed, 1 insertion(+), 67 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs deleted file mode 100644 index 72763421cc..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneControlPointConversion.cs +++ /dev/null @@ -1,38 +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 NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Objects; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Taiko.Tests -{ - public class TestSceneControlPointConversion : OsuTestScene - { - [Test] - public void TestSceneScrollSpeedConversion() - { - const double start_time = 1000; - const double slider_velocity = 10; - - var beatmap = new Beatmap - { - HitObjects = - { - new HitObject - { - StartTime = start_time, - DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = slider_velocity } - } - }, - BeatmapInfo = { Ruleset = { OnlineID = 0 } }, - }; - - var convertedBeatmap = new TaikoRuleset().CreateBeatmapConverter(beatmap).Convert(); - - AddAssert("effect point generated", () => convertedBeatmap.ControlPointInfo.EffectPointAt(start_time).ScrollSpeed == slider_velocity); - } - } -} diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index c1e1052569..524565a863 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -57,29 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Beatmap converted = base.ConvertBeatmap(original, cancellationToken); - if (original.BeatmapInfo.Ruleset.OnlineID == 0) - { - // Post processing step to transform standard slider velocity changes into scroll speed changes - double lastScrollSpeed = 1; - - foreach (HitObject hitObject in original.HitObjects) - { - double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity; - - if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed)) - { - EffectControlPoint currentControlPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime); - - if (Precision.AlmostEquals(currentControlPoint.Time, hitObject.StartTime)) - currentControlPoint.ScrollSpeed = nextScrollSpeed; - else - converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint { ScrollSpeed = nextScrollSpeed }); - - lastScrollSpeed = nextScrollSpeed; - } - } - } - if (original.BeatmapInfo.Ruleset.OnlineID == 3) { // Post processing step to transform mania hit objects with the same start time into strong hits diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 75500fbc4e..2d9320d9c4 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -438,14 +438,9 @@ namespace osu.Game.Beatmaps.Formats { KiaiMode = kiaiMode, OmitFirstBarLine = omitFirstBarSignature, + ScrollSpeed = speedMultiplier, }; - int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; - - // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. - if (onlineRulesetID == 1 || onlineRulesetID == 3) - effectPoint.ScrollSpeed = speedMultiplier; - addControlPoint(time, effectPoint, timingChange); addControlPoint(time, new LegacySampleControlPoint From aae5359b2e5e33cb37404dda1e580a3ee77691b1 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 25 Oct 2022 17:47:51 +0200 Subject: [PATCH 212/321] Update Tests --- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +- osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index fdd0167ed3..d9bbe7a51e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, controlPoints.TimingPoints.Count); Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); Assert.AreEqual(34, controlPoints.SamplePoints.Count); - Assert.AreEqual(8, controlPoints.EffectPoints.Count); + Assert.AreEqual(13, controlPoints.EffectPoints.Count); var timingPoint = controlPoints.TimingPointAt(0); Assert.AreEqual(956, timingPoint.Time); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index 03c184c27d..e93382cc9b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -67,8 +67,8 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); - AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670); - AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 54670); + AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 37560); + AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 37560); AddStep("Seek to just before next point", () => EditorClock.Seek(69000)); AddStep("Start clock", () => EditorClock.Start()); @@ -85,8 +85,8 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); - AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670); - AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 54670); + AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 37560); + AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 37560); AddStep("Seek to later", () => EditorClock.Seek(80000)); AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 69670); From d2b0a413da43e15ea6964fa6650a15ec89d068a7 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 25 Oct 2022 18:18:25 +0200 Subject: [PATCH 213/321] Always encode scroll speed as slider velocity in the legacy encoder --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 03c63ff4f2..c9ee63424c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -185,19 +185,12 @@ namespace osu.Game.Beatmaps.Formats SampleControlPoint lastRelevantSamplePoint = null; DifficultyControlPoint lastRelevantDifficultyPoint = null; - // In osu!taiko and osu!mania, a scroll speed is stored as "slider velocity" in legacy formats. - // In that case, a scrolling speed change is a global effect and per-hit object difficulty control points are ignored. - bool scrollSpeedEncodedAsSliderVelocity = onlineRulesetID == 1 || onlineRulesetID == 3; - // iterate over hitobjects and pull out all required sample and difficulty changes extractDifficultyControlPoints(beatmap.HitObjects); extractSampleControlPoints(beatmap.HitObjects); - if (scrollSpeedEncodedAsSliderVelocity) - { - foreach (var point in legacyControlPoints.EffectPoints) - legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); - } + foreach (var point in legacyControlPoints.EffectPoints) + legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); foreach (var group in legacyControlPoints.Groups) { @@ -244,9 +237,6 @@ namespace osu.Game.Beatmaps.Formats IEnumerable collectDifficultyControlPoints(IEnumerable hitObjects) { - if (scrollSpeedEncodedAsSliderVelocity) - yield break; - foreach (var hitObject in hitObjects) yield return hitObject.DifficultyControlPoint; } From 93e9b4a2c5329a46668b6f4261680abfb763a7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Oct 2022 19:53:05 +0200 Subject: [PATCH 214/321] Add failing test case --- .../Mods/TestSceneTaikoModFlashlight.cs | 36 +++++++++++++++++++ .../Mods/TaikoModFlashlight.cs | 8 ++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs index 417b59f5d2..d55ce17e6c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModFlashlight.cs @@ -1,8 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; +using osu.Framework.Testing; using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.UI; +using osuTK; namespace osu.Game.Rulesets.Taiko.Tests.Mods { @@ -16,5 +20,37 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods [Test] public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new TaikoModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + + [Test] + public void TestFlashlightAlwaysHasNonZeroSize() + { + bool failed = false; + + CreateModTest(new ModTestData + { + Mod = new TestTaikoModFlashlight { ComboBasedSize = { Value = true } }, + Autoplay = false, + PassCondition = () => + { + failed |= this.ChildrenOfType().SingleOrDefault()?.FlashlightSize.Y == 0; + return !failed; + } + }); + } + + private class TestTaikoModFlashlight : TaikoModFlashlight + { + protected override Flashlight CreateFlashlight() => new TestTaikoFlashlight(this, Playfield); + + public class TestTaikoFlashlight : TaikoFlashlight + { + public TestTaikoFlashlight(TaikoModFlashlight modFlashlight, TaikoPlayfield taikoPlayfield) + : base(modFlashlight, taikoPlayfield) + { + } + + public new Vector2 FlashlightSize => base.FlashlightSize; + } + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 98f954ad29..d5b0b64036 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -27,17 +27,17 @@ namespace osu.Game.Rulesets.Taiko.Mods public override float DefaultFlashlightSize => 200; - protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, playfield); + protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, Playfield); - private TaikoPlayfield playfield = null!; + protected TaikoPlayfield Playfield { get; private set; } = null!; public override void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - playfield = (TaikoPlayfield)drawableRuleset.Playfield; + Playfield = (TaikoPlayfield)drawableRuleset.Playfield; base.ApplyToDrawableRuleset(drawableRuleset); } - private class TaikoFlashlight : Flashlight + public class TaikoFlashlight : Flashlight { private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo); private readonly TaikoPlayfield taikoPlayfield; From e3bc1126d75e204b08a85059b6f818c4c292d60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Oct 2022 20:03:22 +0200 Subject: [PATCH 215/321] Fix invalid flashlight size calculation This regressed in #20714. One usage of `getSizeFor(0)` was replaced by `adjustSize(GetSize())`, but another usage of `getSizeFor(Combo.Value)` was replaced by `adjustSize(Combo.Value)`, which is not correct, since `adjustSize()` is expecting to receive a combo-based size, rather than a combo value directly. --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index d5b0b64036..2b3dc799ab 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Mods FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre); ClearTransforms(targetMember: nameof(FlashlightSize)); - FlashlightSize = adjustSize(Combo.Value); + FlashlightSize = adjustSize(GetSize()); flashlightProperties.Validate(); } From 974f22dd97a4f79fc9709a5431c2f1ecae73549f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Oct 2022 20:11:29 +0200 Subject: [PATCH 216/321] Attempt to make flashlight code slightly more legible --- .../Mods/TaikoModFlashlight.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 2b3dc799ab..46569c2495 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -47,21 +47,28 @@ namespace osu.Game.Rulesets.Taiko.Mods { this.taikoPlayfield = taikoPlayfield; - FlashlightSize = adjustSize(GetSize()); + FlashlightSize = adjustSizeForPlayfieldAspectRatio(GetSize()); FlashlightSmoothness = 1.4f; AddLayout(flashlightProperties); } - private Vector2 adjustSize(float size) + /// + /// Returns the aspect ratio-adjusted size of the flashlight. + /// This ensures that the size of the flashlight remains independent of taiko-specific aspect ratio adjustments. + /// + /// + /// The size of the flashlight. + /// The value provided here should always come from . + /// + private Vector2 adjustSizeForPlayfieldAspectRatio(float size) { - // Preserve flashlight size through the playfield's aspect adjustment. return new Vector2(0, size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT); } protected override void UpdateFlashlightSize(float size) { - this.TransformTo(nameof(FlashlightSize), adjustSize(size), FLASHLIGHT_FADE_DURATION); + this.TransformTo(nameof(FlashlightSize), adjustSizeForPlayfieldAspectRatio(size), FLASHLIGHT_FADE_DURATION); } protected override string FragmentShader => "CircularFlashlight"; @@ -75,7 +82,7 @@ namespace osu.Game.Rulesets.Taiko.Mods FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre); ClearTransforms(targetMember: nameof(FlashlightSize)); - FlashlightSize = adjustSize(GetSize()); + FlashlightSize = adjustSizeForPlayfieldAspectRatio(GetSize()); flashlightProperties.Validate(); } From f5ca447b8e289712a9ffe8c6ff164f2d36aa87e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 25 Oct 2022 21:34:41 +0200 Subject: [PATCH 217/321] Rename one more "{duplicate -> clone}" reference --- osu.Game/Screens/Edit/Editor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c2899bc1ec..912681e114 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -304,7 +304,7 @@ namespace osu.Game.Screens.Edit cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), - duplicateMenuItem = new EditorMenuItem("Clone", MenuItemType.Standard, Clone), + cloneMenuItem = new EditorMenuItem("Clone", MenuItemType.Standard, Clone), } }, new MenuItem("View") @@ -746,7 +746,7 @@ namespace osu.Game.Screens.Edit private EditorMenuItem cutMenuItem; private EditorMenuItem copyMenuItem; - private EditorMenuItem duplicateMenuItem; + private EditorMenuItem cloneMenuItem; private EditorMenuItem pasteMenuItem; private readonly BindableWithCurrent canCut = new BindableWithCurrent(); @@ -759,7 +759,7 @@ namespace osu.Game.Screens.Edit canCopy.Current.BindValueChanged(copy => { copyMenuItem.Action.Disabled = !copy.NewValue; - duplicateMenuItem.Action.Disabled = !copy.NewValue; + cloneMenuItem.Action.Disabled = !copy.NewValue; }, true); canPaste.Current.BindValueChanged(paste => pasteMenuItem.Action.Disabled = !paste.NewValue, true); } From 8505848b1a0efaf3682a7b703b94f95f95d902c2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 25 Oct 2022 18:06:49 -0700 Subject: [PATCH 218/321] Scroll beatmap listing to top when searching via tags/source --- osu.Game/Overlays/BeatmapListingOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 2be328427b..c73936da8a 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -115,6 +115,7 @@ namespace osu.Game.Overlays { filterControl.Search(query); Show(); + ScrollFlow.ScrollToStart(); } protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader(); From d9d083752aeac9d1342084f63d6495d5a0e78a8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 11:03:11 +0900 Subject: [PATCH 219/321] Clamp backwards index to total current items to fix edge case of item removal --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 34865c7f51..6366fc8050 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Select.Carousel return null; int forwardsIndex = lastSelectedIndex; - int backwardsIndex = lastSelectedIndex; + int backwardsIndex = Math.Min(lastSelectedIndex, Items.Count - 1); while (true) { From cbcebfa130f342f3900b86542e72ed8c2098a287 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 12:18:54 +0900 Subject: [PATCH 220/321] Remove switch back to selection tool to simplify test flow --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index f6d3512fe6..f9cea5761b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -79,8 +79,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("placement blueprint at (0, 0)", () => Precision.AlmostEquals(Editor.ChildrenOfType().Single().HitObject.Position, new Vector2(0, 0))); else AddAssert("placement blueprint at (1, 1)", () => Precision.AlmostEquals(Editor.ChildrenOfType().Single().HitObject.Position, new Vector2(1, 1))); - - AddStep("choose selection tool", () => InputManager.Key(Key.Number1)); } [Test] From 5f10b81af1dfaf0f717858961780538a1fc2498d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 12:55:59 +0900 Subject: [PATCH 221/321] Move distance snap grid test cursor behind grid to better visualise centre-points --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index dc74d38cdc..f880739950 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, + new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }), - new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position } }; }); @@ -166,8 +166,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, + new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }), - new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position } }; }); From cab9a1c01203236748b6a72077dec351286d5d34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:08:19 +0900 Subject: [PATCH 222/321] Add test coverage of distance snap grid failing visually --- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index f880739950..1b67fc2ca9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -4,12 +4,15 @@ #nullable disable using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Overlays; @@ -52,6 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }; private OsuDistanceSnapGrid grid; + private SnappingCursorContainer cursor; public TestSceneOsuDistanceSnapGrid() { @@ -88,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, + cursor = new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }), }; }); @@ -154,6 +158,37 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertSnappedDistance(expectedDistance); } + [Test] + public void TestReferenceObjectNotOnSnapGrid() + { + AddStep("create grid", () => + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.SlateGray + }, + cursor = new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, + grid = new OsuDistanceSnapGrid(new HitCircle + { + Position = grid_position, + // This is important. It sets the reference object to a point in time that isn't on the current snap divisor's grid. + // We are testing that the grid's display is offset correctly. + StartTime = 40, + }), + }; + }); + + AddStep("move mouse to point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 2))); + + AddAssert("Ensure cursor is on a grid line", () => + { + return grid.ChildrenOfType().Any(p => Precision.AlmostEquals(p.ScreenSpaceDrawQuad.TopRight.X, grid.ToScreenSpace(cursor.LastSnappedPosition).X)); + }); + } + [Test] public void TestLimitedDistance() { @@ -166,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, + cursor = new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }, grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }), }; }); @@ -186,6 +221,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { public Func GetSnapPosition; + public Vector2 LastSnappedPosition { get; private set; } + private readonly Drawable cursor; private InputManager inputManager; @@ -214,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor protected override void Update() { base.Update(); - cursor.Position = GetSnapPosition.Invoke(inputManager.CurrentState.Mouse.Position); + cursor.Position = LastSnappedPosition = GetSnapPosition.Invoke(inputManager.CurrentState.Mouse.Position); } } } From 2016d330553bc1dc4ebbe3135011e71454590b8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:10:39 +0900 Subject: [PATCH 223/321] Offset start of distance snap grid drawing if reference object's start time doesn't align Closes #20938. --- .../Edit/Compose/Components/CircularDistanceSnapGrid.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 98079116cd..6e54e98740 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -53,9 +53,16 @@ namespace osu.Game.Screens.Edit.Compose.Components float maxDistance = new Vector2(dx, dy).Length; int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTicks)); + // We need to offset the drawn lines to the next valid snap for the currently selected divisor. + // + // Picture the scenario where the user has just placed an object on a 1/2 snap, then changes to + // 1/3 snap and expects to be able to place the next object on a valid 1/3 snap, regardless of the + // fact that the 1/2 snap reference object is not valid for 1/3 snapping. + float offset = SnapProvider.FindSnappedDistance(ReferenceObject, 0); + for (int i = 0; i < requiredCircles; i++) { - float diameter = (i + 1) * DistanceBetweenTicks * 2; + float diameter = (offset + (i + 1) * DistanceBetweenTicks) * 2; AddInternal(new Ring(ReferenceObject, GetColourForIndexFromPlacement(i)) { From 8d9a85e9e588b1cc964ecc6514ed1d105b713c10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:14:12 +0900 Subject: [PATCH 224/321] Fix typos in xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Rulesets/Edit/ExpandableButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/ExpandableButton.cs b/osu.Game/Rulesets/Edit/ExpandableButton.cs index 5b60a2536d..a66600bd58 100644 --- a/osu.Game/Rulesets/Edit/ExpandableButton.cs +++ b/osu.Game/Rulesets/Edit/ExpandableButton.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Edit private LocalisableString contractedLabelText; /// - /// The label text to display when this slider is in a contracted state. + /// The label text to display when this button is in a contracted state. /// public LocalisableString ContractedLabelText { @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Edit private LocalisableString expandedLabelText; /// - /// The label text to display when this slider is in an expanded state. + /// The label text to display when this button is in an expanded state. /// public LocalisableString ExpandedLabelText { From 54ae16badc5eeeb5c5f63cc59a3b3a48832da203 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:27:23 +0900 Subject: [PATCH 225/321] Move distance snap toggle button implementation to `DistancedHitObjectComposer` --- .../Edit/CatchHitObjectComposer.cs | 39 +------------------ .../Edit/OsuHitObjectComposer.cs | 19 ++------- .../Edit/DistancedHitObjectComposer.cs | 39 +++++++++++++++++++ 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 9ba17c89ff..721ef64dc0 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -10,7 +10,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Beatmaps; @@ -23,7 +22,6 @@ using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -35,8 +33,6 @@ namespace osu.Game.Rulesets.Catch.Edit private CatchDistanceSnapGrid distanceSnapGrid; - private readonly Bindable distanceSnapToggle = new Bindable(); - private InputManager inputManager; private readonly BindableDouble timeRangeMultiplier = new BindableDouble(1) @@ -88,34 +84,6 @@ namespace osu.Game.Rulesets.Catch.Edit updateDistanceSnapGrid(); } - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.Repeat) - return false; - - handleToggleViaKey(e); - return base.OnKeyDown(e); - } - - protected override void OnKeyUp(KeyUpEvent e) - { - handleToggleViaKey(e); - base.OnKeyUp(e); - } - - private bool distanceSnapMomentary; - - private void handleToggleViaKey(KeyboardEvent key) - { - bool altPressed = key.AltPressed; - - if (altPressed != distanceSnapMomentary) - { - distanceSnapMomentary = altPressed; - distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - } - } - public override bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) @@ -148,11 +116,6 @@ namespace osu.Game.Rulesets.Catch.Edit new BananaShowerCompositionTool() }; - protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] - { - new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) - }); - public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { var result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); @@ -224,7 +187,7 @@ namespace osu.Game.Rulesets.Catch.Edit private void updateDistanceSnapGrid() { - if (distanceSnapToggle.Value != TernaryState.True) + if (DistanceSnapToggle.Value != TernaryState.True) { distanceSnapGrid.Hide(); return; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index bf7cddd099..a543cb62fa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -46,12 +46,10 @@ namespace osu.Game.Rulesets.Osu.Edit new SpinnerCompositionTool() }; - private readonly Bindable distanceSnapToggle = new Bindable(); private readonly Bindable rectangularGridSnapToggle = new Bindable(); protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] { - new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }), new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th }) }); @@ -82,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit placementObject = EditorBeatmap.PlacementObject.GetBoundCopy(); placementObject.ValueChanged += _ => updateDistanceSnapGrid(); - distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid(); + DistanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid(); // we may be entering the screen with a selection already active updateDistanceSnapGrid(); @@ -128,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (snapType.HasFlagFast(SnapType.Grids)) { - if (distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) + if (DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) { (Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition)); @@ -197,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit distanceSnapGridCache.Invalidate(); distanceSnapGrid = null; - if (distanceSnapToggle.Value != TernaryState.True) + if (DistanceSnapToggle.Value != TernaryState.True) return; switch (BlueprintContainer.CurrentTool) @@ -242,24 +240,15 @@ namespace osu.Game.Rulesets.Osu.Edit protected override bool AdjustDistanceSpacing(GlobalAction action, float amount) { // To allow better visualisation, ensure that the spacing grid is visible before adjusting. - distanceSnapToggle.Value = TernaryState.True; + DistanceSnapToggle.Value = TernaryState.True; return base.AdjustDistanceSpacing(action, amount); } - private bool distanceSnapMomentary; private bool gridSnapMomentary; private void handleToggleViaKey(KeyboardEvent key) { - bool altPressed = key.AltPressed; - - if (altPressed != distanceSnapMomentary) - { - distanceSnapMomentary = altPressed; - distanceSnapToggle.Value = distanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; - } - bool shiftPressed = key.ShiftPressed; if (shiftPressed != gridSnapMomentary) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 449996131d..cd7c25509c 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -3,6 +3,8 @@ #nullable disable +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -10,6 +12,7 @@ using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; @@ -20,6 +23,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.OSD; using osu.Game.Overlays.Settings.Sections; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit.Components.TernaryButtons; namespace osu.Game.Rulesets.Edit { @@ -48,6 +52,10 @@ namespace osu.Game.Rulesets.Edit [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } + protected readonly Bindable DistanceSnapToggle = new Bindable(); + + private bool distanceSnapMomentary; + protected DistancedHitObjectComposer(Ruleset ruleset) : base(ruleset) { @@ -105,6 +113,37 @@ namespace osu.Game.Rulesets.Edit } } + protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] + { + new TernaryButton(DistanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) + }); + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Repeat) + return false; + + handleToggleViaKey(e); + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + handleToggleViaKey(e); + base.OnKeyUp(e); + } + + private void handleToggleViaKey(KeyboardEvent key) + { + bool altPressed = key.AltPressed; + + if (altPressed != distanceSnapMomentary) + { + distanceSnapMomentary = altPressed; + DistanceSnapToggle.Value = DistanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + } + } + public virtual bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) From 6b53ea3400511e74b8c108189f0014a73eaa4f63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:37:01 +0900 Subject: [PATCH 226/321] Enable distance snapping when DS value is changed via user interaction --- .../Rulesets/Edit/DistancedHitObjectComposer.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index ab320af902..ca7ca79813 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -89,8 +89,9 @@ namespace osu.Game.Rulesets.Edit { distanceSpacingSlider = new ExpandableSlider> { - Current = { BindTarget = DistanceSpacingMultiplier }, KeyboardStep = adjust_step, + // Manual binding in LoadComplete to handle one-way event flow. + Current = DistanceSpacingMultiplier.GetUnboundCopy(), }, currentDistanceSpacingButton = new ExpandableButton { @@ -101,6 +102,7 @@ namespace osu.Game.Rulesets.Edit Debug.Assert(objects != null); DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after); + DistanceSnapToggle.Value = TernaryState.True; }, RelativeSizeAxes = Axes.X, } @@ -173,6 +175,14 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.BeatmapInfo.DistanceSpacing = multiplier.NewValue; }, true); + + // Manual binding to handle enabling distance spacing when the slider is interacted with. + distanceSpacingSlider.Current.BindValueChanged(spacing => + { + DistanceSpacingMultiplier.Value = spacing.NewValue; + DistanceSnapToggle.Value = TernaryState.True; + }); + DistanceSpacingMultiplier.BindValueChanged(spacing => distanceSpacingSlider.Current.Value = spacing.NewValue); } } @@ -245,6 +255,7 @@ namespace osu.Game.Rulesets.Edit else if (action == GlobalAction.EditorDecreaseDistanceSpacing) DistanceSpacingMultiplier.Value -= amount; + DistanceSnapToggle.Value = TernaryState.True; return true; } From f8a4af5e0e400067471fb0a301cf945fcdaba023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 13:45:06 +0900 Subject: [PATCH 227/321] Add osu!catch read-distance-spacing implementation --- .../Edit/CatchHitObjectComposer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 004919e807..220bc49203 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; @@ -79,8 +80,15 @@ namespace osu.Game.Rulesets.Catch.Edit protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) { - // TODO: catch lol - return 1; + // osu!catch's distance snap implementation is limited, in that a custom spacing cannot be specified. + // Therefore this functionality is not currently used. + // + // The implementation below is probably correct but should be checked if/when exposed via controls. + + float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime()); + float actualDistance = Math.Abs(((CatchHitObject)before).EffectiveX - ((CatchHitObject)after).EffectiveX); + + return actualDistance / expectedDistance; } protected override void Update() From d03161ec013ad0a1b670aad474158c3349e85352 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 14:39:39 +0900 Subject: [PATCH 228/321] Apply distance snap time component even when nearby object snap kicks in --- .../Edit/OsuHitObjectComposer.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 67061bdaf2..f9a952c2fb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -13,6 +13,7 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -120,7 +121,24 @@ namespace osu.Game.Rulesets.Osu.Edit public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) { if (snapType.HasFlagFast(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) + { + // In the case of snapping to nearby objects, a time value is not provided. + // This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap + // this could result in unexpected behaviour when distance snapping is turned on an a user attempts to place an object that is + // BOTH on a valid distance snap ring, and also at the same position as a previous object. + // + // We want to ensure that in this particular case, the time-snapping component of distance snap is still applied. + // The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over + // the time value if the proposed positions are roughly the same. + if (snapType.HasFlagFast(SnapType.Grids) && distanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) + { + (Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition)); + if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1)) + snapResult.Time = distanceSnappedTime; + } + return snapResult; + } SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); From 614011d612b760d94b97f6d23df360a75b6649d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 15:25:50 +0900 Subject: [PATCH 229/321] Update `TestSceneObjectOrderedHitPolicy` to provide better output --- osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs index 1665c40b40..ed1891b7d9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs @@ -377,7 +377,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void addJudgementAssert(OsuHitObject hitObject, HitResult result) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}", - () => judgementResults.Single(r => r.HitObject == hitObject).Type == result); + () => judgementResults.Single(r => r.HitObject == hitObject).Type, () => Is.EqualTo(result)); } private void addJudgementAssert(string name, Func hitObject, HitResult result) From 674ae9e7427e06dfbffb9287a0c5f7a39fa11f3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 15:26:20 +0900 Subject: [PATCH 230/321] Refactor `DrawableSliderHead` to never update the drawable position Slider heads are guaranteed to always be drawn at (0,0). This fixes weird behaviour in the editor, but also simplifies things in the process. Win-win. Closes #20644. --- .../Objects/Drawables/DrawableHitCircle.cs | 9 +++++++-- .../Objects/Drawables/DrawableSliderHead.cs | 19 +++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 23db29b9a6..841a52da7b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -102,8 +102,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = HitArea.DrawSize; - PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); + PositionBindable.BindValueChanged(_ => UpdatePosition()); + StackHeightBindable.BindValueChanged(_ => UpdatePosition()); ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); } @@ -134,6 +134,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + protected virtual void UpdatePosition() + { + Position = HitObject.StackedPosition; + } + public override void Shake() => shakeContainer.Shake(); protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 80b9544e5b..d1d749d7e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -6,7 +6,6 @@ using System; using System.Diagnostics; using JetBrains.Annotations; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -43,13 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } - [BackgroundDependencyLoader] - private void load() - { - PositionBindable.BindValueChanged(_ => updatePosition()); - pathVersion.BindValueChanged(_ => updatePosition()); - } - protected override void OnFree() { base.OnFree(); @@ -57,6 +49,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables pathVersion.UnbindFrom(DrawableSlider.PathVersion); } + protected override void UpdatePosition() + { + // Slider head is always drawn at (0,0). + } + protected override void OnApply() { base.OnApply(); @@ -100,11 +97,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.Shake(); DrawableSlider.Shake(); } - - private void updatePosition() - { - if (Slider != null) - Position = HitObject.Position - Slider.Position; - } } } From 81bb00c25829fca1d394775961bb8e10e28cec2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 16:57:45 +0900 Subject: [PATCH 231/321] Add argon transformer for osu!catch (and basic new catcher design) --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 + .../Skinning/Argon/ArgonCatcher.cs | 86 ++++++++ .../Skinning/Argon/ArgonJudgementPiece.cs | 193 ++++++++++++++++++ .../Argon/CatchArgonSkinTransformer.cs | 34 +++ 4 files changed, 317 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 5c9c95827a..e0f7820262 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Catch.Edit; using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Scoring; +using osu.Game.Rulesets.Catch.Skinning.Argon; using osu.Game.Rulesets.Catch.Skinning.Legacy; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; @@ -188,6 +189,9 @@ namespace osu.Game.Rulesets.Catch { case LegacySkin: return new CatchLegacySkinTransformer(skin); + + case ArgonSkin: + return new CatchArgonSkinTransformer(skin); } return null; diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs new file mode 100644 index 0000000000..4e3439cb2b --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Catch.UI; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + public class ArgonCatcher : CompositeDrawable + { + [Resolved] + private Bindable currentState { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Height = 10, + Children = new Drawable[] + { + new Circle + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Colour = Color4.White, + Alpha = 0.25f, + RelativeSizeAxes = Axes.X, + X = -2, + Width = 20, + Height = 1.8f, + }, + new Circle + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + Colour = Color4.White, + RelativeSizeAxes = Axes.X, + X = -2, + Width = 15 / 170f, + Height = 4, + }, + new Box + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + Colour = Color4.White, + Alpha = 0.25f, + RelativeSizeAxes = Axes.X, + X = 2, + Width = 20, + Height = 1.8f, + }, + new Circle + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreLeft, + Colour = Color4.White, + X = 2, + RelativeSizeAxes = Axes.X, + Width = 15 / 170f, + Height = 4, + }, + } + }, + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs new file mode 100644 index 0000000000..59e8b5a0b3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs @@ -0,0 +1,193 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + public class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement + { + protected readonly HitResult Result; + + protected SpriteText JudgementText { get; private set; } = null!; + + private RingExplosion? ringExplosion; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + public ArgonJudgementPiece(HitResult result) + { + Result = result; + Origin = Anchor.Centre; + Y = 160; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + JudgementText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = Result.GetDescription().ToUpperInvariant(), + Colour = colours.ForHitResult(Result), + Blending = BlendingParameters.Additive, + Spacing = new Vector2(10, 0), + Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular), + }, + }; + + if (Result.IsHit()) + { + AddInternal(ringExplosion = new RingExplosion(Result) + { + Colour = colours.ForHitResult(Result), + }); + } + } + + /// + /// Plays the default animation for this judgement piece. + /// + /// + /// The base implementation only handles fade (for all result types) and misses. + /// Individual rulesets are recommended to implement their appropriate hit animations. + /// + public virtual void PlayAnimation() + { + switch (Result) + { + default: + JudgementText + .ScaleTo(Vector2.One) + .ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint); + break; + + case HitResult.Miss: + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); + + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + + this.RotateTo(0); + this.RotateTo(40, 800, Easing.InQuint); + break; + } + + this.FadeOutFromOne(800); + + ringExplosion?.PlayAnimation(); + } + + public Drawable? GetAboveHitObjectsProxiedContent() => null; + + private class RingExplosion : CompositeDrawable + { + private readonly float travel = 52; + + public RingExplosion(HitResult result) + { + const float thickness = 4; + + const float small_size = 9; + const float large_size = 14; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Blending = BlendingParameters.Additive; + + int countSmall = 0; + int countLarge = 0; + + switch (result) + { + case HitResult.Meh: + countSmall = 3; + travel *= 0.3f; + break; + + case HitResult.Ok: + case HitResult.Good: + countSmall = 4; + travel *= 0.6f; + break; + + case HitResult.Great: + case HitResult.Perfect: + countSmall = 4; + countLarge = 4; + break; + } + + for (int i = 0; i < countSmall; i++) + AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) }); + + for (int i = 0; i < countLarge; i++) + AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) }); + } + + public void PlayAnimation() + { + foreach (var c in InternalChildren) + { + const float start_position_ratio = 0.3f; + + float direction = RNG.NextSingle(0, 360); + float distance = RNG.NextSingle(travel / 2, travel); + + c.MoveTo(new Vector2( + MathF.Cos(direction) * distance * start_position_ratio, + MathF.Sin(direction) * distance * start_position_ratio + )); + + c.MoveTo(new Vector2( + MathF.Cos(direction) * distance, + MathF.Sin(direction) * distance + ), 600, Easing.OutQuint); + } + + this.FadeOutFromOne(1000, Easing.OutQuint); + } + + public class RingPiece : CircularContainer + { + public RingPiece(float thickness = 9) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + Masking = true; + BorderThickness = thickness; + BorderColour = Color4.White; + + Child = new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + }; + } + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs new file mode 100644 index 0000000000..764f3c6cbd --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -0,0 +1,34 @@ +// 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.Skinning; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + public class CatchArgonSkinTransformer : SkinTransformer + { + public CatchArgonSkinTransformer(ISkin skin) + : base(skin) + { + } + + public override Drawable? GetDrawableComponent(ISkinComponent component) + { + switch (component) + { + case CatchSkinComponent osuComponent: + // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. + switch (osuComponent.Component) + { + case CatchSkinComponents.Catcher: + return new ArgonCatcher(); + } + + break; + } + + return base.GetDrawableComponent(component); + } + } +} From 4ff4de00b3f7eff901ff318ee26f0a1ed160af88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 16:08:01 +0900 Subject: [PATCH 232/321] Add argon fruit design --- .../Skinning/Argon/ArgonFruitPiece.cs | 104 ++++++++++++++++++ .../Argon/CatchArgonSkinTransformer.cs | 3 + .../Skinning/Default/CatchHitObjectPiece.cs | 3 +- .../Skinning/Default/DropletPiece.cs | 2 +- .../Skinning/Default/FruitPiece.cs | 4 +- 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs new file mode 100644 index 0000000000..7a59254028 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Skinning.Default; +using osu.Game.Rulesets.Catch.UI; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + internal class ArgonFruitPiece : CatchHitObjectPiece + { + public readonly Bindable VisualRepresentation = new Bindable(); + + protected override Drawable HyperBorderPiece => hyperBorderPiece; + + private Drawable hyperBorderPiece = null!; + + private Container layers = null!; + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + + Texture largeTexture = getTexture("A"); + + InternalChildren = new[] + { + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + }, + layers = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Alpha = 0.15f, + Texture = largeTexture + }, + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Alpha = 0.5f, + Texture = getTexture("B") + }, + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Texture = getTexture("C") + }, + } + }, + hyperBorderPiece = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, + Blending = BlendingParameters.Additive, + Alpha = 0.15f, + Texture = largeTexture, + }, + }; + + Texture getTexture(string type) => textures.Get($"Gameplay/catch/blob-{type}{RNG.Next(1, 7)}"); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + IndexInBeatmap.BindValueChanged(index => + { + VisualRepresentation.Value = Fruit.GetVisualRepresentation(index.NewValue); + }, true); + + AccentColour.BindValueChanged(colour => + { + foreach (var sprite in layers) + sprite.Colour = colour.NewValue; + }, true); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs index 764f3c6cbd..621a71b1d1 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -23,6 +23,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { case CatchSkinComponents.Catcher: return new ArgonCatcher(); + + case CatchSkinComponents.Fruit: + return new ArgonFruitPiece(); } break; diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index 6cc5220699..fd1047f27e 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -7,6 +7,7 @@ using System; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.Objects.Drawables; using osuTK.Graphics; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default /// A part of this piece that will be only visible when is true. /// [CanBeNull] - protected virtual HyperBorderPiece HyperBorderPiece => null; + protected virtual Drawable HyperBorderPiece => null; protected override void LoadComplete() { diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs index 6b7f25eed1..e4c2f04476 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { public class DropletPiece : CatchHitObjectPiece { - protected override HyperBorderPiece HyperBorderPiece { get; } + protected override Drawable HyperBorderPiece { get; } public DropletPiece() { diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 8fb5c8f84a..9dce0e9129 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default public readonly Bindable VisualRepresentation = new Bindable(); protected override BorderPiece BorderPiece { get; } - protected override HyperBorderPiece HyperBorderPiece { get; } + protected override Drawable HyperBorderPiece { get; } public FruitPiece() { RelativeSizeAxes = Axes.Both; - InternalChildren = new Drawable[] + InternalChildren = new[] { new FruitPulpFormation { From a475ca848d188d9ac490d4b3a6f6edc3c5250f5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 16:31:40 +0900 Subject: [PATCH 233/321] Add rotation to fruit pieces --- .../Skinning/Argon/ArgonFruitPiece.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs index 7a59254028..9561659792 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs @@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon private Container layers = null!; + private float rotationRandomness; + [BackgroundDependencyLoader] private void load(TextureStore textures) { @@ -99,6 +101,24 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon foreach (var sprite in layers) sprite.Colour = colour.NewValue; }, true); + + rotationRandomness = RNG.NextSingle(0.2f, 1) * (RNG.NextBool() ? -1 : 1); + } + + protected override void Update() + { + base.Update(); + + for (int i = 0; i < layers.Count; i++) + { + layers[i].Rotation += + // Layers are ordered from largest to smallest. Smaller layers should rotate more. + (i * 2) + * (float)Clock.ElapsedFrameTime + * 0.02f * rotationRandomness + // Each layer should alternate rotation direction. + * (i % 2 == 1 ? 1 : -1); + } } } } From 0a8a13f52915e90f30b60a62aecc048378cad645 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 17:01:58 +0900 Subject: [PATCH 234/321] Add argon banana piece --- .../Skinning/Argon/ArgonBananaPiece.cs | 82 +++++++++++++++++++ .../Argon/CatchArgonSkinTransformer.cs | 3 + 2 files changed, 85 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs new file mode 100644 index 0000000000..49edc80770 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs @@ -0,0 +1,82 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + internal class ArgonBananaPiece : ArgonFruitPiece + { + [BackgroundDependencyLoader] + private void load() + { + AddInternal(new UprightAspectMaintainingContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + new Circle + { + Colour = Color4.White.Opacity(0.4f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Size = new Vector2(8), + Scale = new Vector2(30, 1), + }, + new Box + { + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White), + RelativeSizeAxes = Axes.X, + Blending = BlendingParameters.Additive, + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + Width = 1.6f, + Height = 2, + }, + new Box + { + Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)), + RelativeSizeAxes = Axes.X, + Blending = BlendingParameters.Additive, + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + Width = 1.6f, + Height = 2, + }, + new Circle + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(1.2f), + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Hollow = false, + Colour = Color4.White.Opacity(0.1f), + Radius = 50, + }, + Child = + { + Alpha = 0, + AlwaysPresent = true, + }, + BorderColour = Color4.White.Opacity(0.1f), + BorderThickness = 3, + }, + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs index 621a71b1d1..b55706b6d0 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -26,6 +26,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon case CatchSkinComponents.Fruit: return new ArgonFruitPiece(); + + case CatchSkinComponents.Banana: + return new ArgonBananaPiece(); } break; From 2deaae270c19d991e7b12fc1280146efa3c11b2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 17:12:20 +0900 Subject: [PATCH 235/321] Fix catcher not consdiering allowable catch range --- .../Skinning/Argon/ArgonCatcher.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs index 4e3439cb2b..ffd99aff4a 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs @@ -34,48 +34,51 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon new Circle { RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = Color4.White, + Width = Catcher.ALLOWED_CATCH_RANGE, }, new Box { + Name = "long line left", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, Colour = Color4.White, Alpha = 0.25f, RelativeSizeAxes = Axes.X, - X = -2, Width = 20, Height = 1.8f, }, new Circle { + Name = "bumper left", Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, + Origin = Anchor.CentreLeft, Colour = Color4.White, RelativeSizeAxes = Axes.X, - X = -2, - Width = 15 / 170f, + Width = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2, Height = 4, }, new Box { + Name = "long line right", Anchor = Anchor.CentreRight, Origin = Anchor.CentreLeft, Colour = Color4.White, Alpha = 0.25f, RelativeSizeAxes = Axes.X, - X = 2, Width = 20, Height = 1.8f, }, new Circle { + Name = "bumper right", Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, + Origin = Anchor.CentreRight, Colour = Color4.White, - X = 2, RelativeSizeAxes = Axes.X, - Width = 15 / 170f, + Width = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2, Height = 4, }, } From 14a4fad6f1c21e14c7d72ec52461524ebb3b5bbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 17:16:38 +0900 Subject: [PATCH 236/321] Remove unused `VisualRepresentation` --- .../Skinning/Argon/ArgonFruitPiece.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs index 9561659792..2412de7dbe 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs @@ -2,14 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Catch.UI; using osuTK; @@ -18,8 +16,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { internal class ArgonFruitPiece : CatchHitObjectPiece { - public readonly Bindable VisualRepresentation = new Bindable(); - protected override Drawable HyperBorderPiece => hyperBorderPiece; private Drawable hyperBorderPiece = null!; @@ -91,11 +87,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { base.LoadComplete(); - IndexInBeatmap.BindValueChanged(index => - { - VisualRepresentation.Value = Fruit.GetVisualRepresentation(index.NewValue); - }, true); - AccentColour.BindValueChanged(colour => { foreach (var sprite in layers) From 8a513003ce38f4212e9f40f90c21ec1555ca1c0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 17:32:26 +0900 Subject: [PATCH 237/321] Add argon droplet --- .../Skinning/Argon/ArgonDropletPiece.cs | 118 ++++++++++++++++++ .../Argon/CatchArgonSkinTransformer.cs | 3 + .../Skinning/Default/DropletPiece.cs | 2 +- 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs new file mode 100644 index 0000000000..c607b96729 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs @@ -0,0 +1,118 @@ +// 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.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Skinning.Default; +using osu.Game.Rulesets.Catch.UI; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + internal class ArgonDropletPiece : CatchHitObjectPiece + { + protected override Drawable HyperBorderPiece => hyperBorderPiece; + + private Drawable hyperBorderPiece = null!; + + private Container layers = null!; + + private float rotationRandomness; + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + + const float droplet_scale_down = 0.5f; + + InternalChildren = new[] + { + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + }, + layers = new Container + { + Scale = new Vector2(droplet_scale_down), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Alpha = 0.15f, + Texture = getTexture("A") + }, + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Alpha = 0.5f, + Scale = new Vector2(0.8f), + Texture = getTexture("A") + }, + } + }, + hyperBorderPiece = new Sprite + { + Scale = new Vector2(droplet_scale_down), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, + Blending = BlendingParameters.Additive, + Alpha = 0.15f, + Texture = getTexture("A"), + }, + }; + + Texture getTexture(string type) => textures.Get($"Gameplay/catch/blob-{type}{RNG.Next(1, 7)}"); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AccentColour.BindValueChanged(colour => + { + foreach (var sprite in layers) + sprite.Colour = colour.NewValue; + }, true); + + rotationRandomness = RNG.NextSingle(0.2f, 1); + } + + protected override void Update() + { + base.Update(); + + // Note that droplets are rotated at a higher level, so this is mostly just to create more + // random arrangements of the multiple layers than actually rotate. + // + // Because underlying rotation is always clockwise, we apply anti-clockwise resistance to avoid + // making things spin too fast. + for (int i = 0; i < layers.Count; i++) + { + layers[i].Rotation -= + // Layers are ordered from largest to smallest. Smaller layers should rotate more. + (i * 4) + * (float)Clock.ElapsedFrameTime + * 0.2f * rotationRandomness + // Each layer should alternate rotation direction. + * (i % 2 == 1 ? 0.5f : 1); + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs index b55706b6d0..76a5b221fd 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -29,6 +29,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon case CatchSkinComponents.Banana: return new ArgonBananaPiece(); + + case CatchSkinComponents.Droplet: + return new ArgonDropletPiece(); } break; diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs index e4c2f04476..b8ae062382 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DropletPiece.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2); - InternalChildren = new Drawable[] + InternalChildren = new[] { new Pulp { From 44f55ed152d21be35ddbf36393d0a919172060de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 18:20:45 +0900 Subject: [PATCH 238/321] Add argon osu!catch hit explosion --- .../Skinning/Argon/ArgonHitExplosion.cs | 112 ++++++++++++++++++ .../Argon/CatchArgonSkinTransformer.cs | 3 + 2 files changed, 115 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs new file mode 100644 index 0000000000..90dca49dfd --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs @@ -0,0 +1,112 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Skinning.Argon +{ + public class ArgonHitExplosion : CompositeDrawable, IHitExplosion + { + public override bool RemoveWhenNotAlive => true; + + private Container tallExplosion = null!; + private Container largeFaint = null!; + + private readonly Bindable accentColour = new Bindable(); + + public ArgonHitExplosion() + { + Size = new Vector2(20); + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + tallExplosion = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Width = 0.1f, + Child = new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }, + }, + largeFaint = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }, + }, + }; + + accentColour.BindValueChanged(colour => + { + tallExplosion.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colour.NewValue, + Hollow = false, + Roundness = 15, + Radius = 15, + }; + + largeFaint.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = Interpolation.ValueAt(0.2f, colour.NewValue, Color4.White, 0, 1), + Hollow = false, + Radius = 50, + }; + }, true); + } + + public void Animate(HitExplosionEntry entry) + { + X = entry.Position; + Scale = new Vector2(entry.HitObject.Scale); + accentColour.Value = entry.ObjectColour; + + using (BeginAbsoluteSequence(entry.LifetimeStart)) + { + this.FadeOutFromOne(400); + + if (!(entry.HitObject is Droplet)) + { + float scale = Math.Clamp(entry.JudgementResult.ComboAtJudgement / 200f, 0.35f, 1.125f); + + tallExplosion + .ScaleTo(new Vector2(1.1f, 20 * scale), 200, Easing.OutQuint) + .Then() + .ScaleTo(new Vector2(1.1f, 1), 600, Easing.In); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs index 76a5b221fd..30542187f2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -21,6 +21,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. switch (osuComponent.Component) { + case CatchSkinComponents.HitExplosion: + return new ArgonHitExplosion(); + case CatchSkinComponents.Catcher: return new ArgonCatcher(); From 8a80cb55bd4741f34d891c6aa0ece5bfa57e583c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Oct 2022 18:55:00 +0900 Subject: [PATCH 239/321] Remove unused catcher animation state for now --- osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs index ffd99aff4a..4db0df4a34 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -13,9 +12,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { public class ArgonCatcher : CompositeDrawable { - [Resolved] - private Bindable currentState { get; set; } = null!; - [BackgroundDependencyLoader] private void load() { From dffebdf7eda6e68045c6e3c99d868507ea06a3dc Mon Sep 17 00:00:00 2001 From: nullium21 Date: Wed, 26 Oct 2022 13:31:32 +0300 Subject: [PATCH 240/321] Only use 0-9A-Za-z-_()[] characters in filenames --- osu.Game/Extensions/ModelExtensions.cs | 31 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index b10071bb45..51bea02726 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; -using System.Linq; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO; @@ -137,20 +136,34 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - private static readonly char[] invalid_filename_characters = Path.GetInvalidFileNameChars() - // Backslash is added to avoid issues when exporting to zip. - // See SharpCompress filename normalisation https://github.com/adamhathcock/sharpcompress/blob/a1e7c0068db814c9aa78d86a94ccd1c761af74bd/src/SharpCompress/Writers/Zip/ZipWriter.cs#L143. - .Append('\\') - .ToArray(); + private static bool isValidFilenameChar(this char ch) + { + if (ch >= '0' && ch <= '9') + return true; + + if (ch >= 'A' && ch <= 'Z') + return true; + + if (ch >= 'a' && ch <= 'z') + return true; + + return "-_()[]".Contains(ch); + } /// /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. /// public static string GetValidArchiveContentFilename(this string filename) { - foreach (char c in invalid_filename_characters) - filename = filename.Replace(c, '_'); - return filename; + char[] resultData = filename.ToCharArray(); + + for (int i = 0; i < resultData.Length; i++) + { + if (!isValidFilenameChar(resultData[i])) + resultData[i] = '_'; + } + + return new string(resultData); } } } From e4f25fc4d49bfd0d91f764c4b54a68c4671404d6 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Wed, 26 Oct 2022 16:42:00 +0200 Subject: [PATCH 241/321] Don't overwrite special precision legacy difficulty points --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index c9ee63424c..b1383c9e85 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Text; using JetBrains.Annotations; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; @@ -190,7 +191,14 @@ namespace osu.Game.Beatmaps.Formats extractSampleControlPoints(beatmap.HitObjects); foreach (var point in legacyControlPoints.EffectPoints) + { + DifficultyControlPoint difficultyPoint = legacyControlPoints.DifficultyPointAt(point.Time); + + if (Precision.AlmostEquals(difficultyPoint.SliderVelocity, point.ScrollSpeed, acceptableDifference: point.ScrollSpeedBindable.Precision)) + continue; + legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); + } foreach (var group in legacyControlPoints.Groups) { From 8b8d0c2293056405941d5b444cb43b7ec5f88ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 26 Oct 2022 23:30:14 +0200 Subject: [PATCH 242/321] Fix typo in comment --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index f9a952c2fb..1675d11896 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Edit { // In the case of snapping to nearby objects, a time value is not provided. // This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap - // this could result in unexpected behaviour when distance snapping is turned on an a user attempts to place an object that is + // this could result in unexpected behaviour when distance snapping is turned on and a user attempts to place an object that is // BOTH on a valid distance snap ring, and also at the same position as a previous object. // // We want to ensure that in this particular case, the time-snapping component of distance snap is still applied. From 0efbae6e70d0190ba42c54c140a4aaecc66be84e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 26 Oct 2022 18:33:07 -0700 Subject: [PATCH 243/321] Fix osu! logo drag area being a square --- osu.Game/Screens/Menu/OsuLogo.cs | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index e9b50f94f7..3efd74d2c8 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { - logoBounceContainer = new DragContainer + logoBounceContainer = new Container { AutoSizeAxes = Axes.Both, Children = new Drawable[] @@ -407,27 +407,24 @@ namespace osu.Game.Screens.Menu impactContainer.ScaleTo(1.12f, 250); } - private class DragContainer : Container + public override bool DragBlocksClick => false; + + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override void OnDrag(DragEvent e) { - public override bool DragBlocksClick => false; + Vector2 change = e.MousePosition - e.MouseDownPosition; - protected override bool OnDragStart(DragStartEvent e) => true; + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.6f) / change.Length; - protected override void OnDrag(DragEvent e) - { - Vector2 change = e.MousePosition - e.MouseDownPosition; + logoBounceContainer.MoveTo(change); + } - // Diminish the drag distance as we go further to simulate "rubber band" feeling. - change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.6f) / change.Length; - - this.MoveTo(change); - } - - protected override void OnDragEnd(DragEndEvent e) - { - this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - base.OnDragEnd(e); - } + protected override void OnDragEnd(DragEndEvent e) + { + logoBounceContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + base.OnDragEnd(e); } } } From 14c7cbde38ce4bada24f6a99076833873026b6a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 11:40:59 +0900 Subject: [PATCH 244/321] Fix toggle mute (and volume meter traversal) handling repeat key presses Closes #20953. --- osu.Game/Overlays/Volume/VolumeControlReceptor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index f2b637c104..624be0b25c 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -23,10 +23,14 @@ namespace osu.Game.Overlays.Volume { case GlobalAction.DecreaseVolume: case GlobalAction.IncreaseVolume: + ActionRequested?.Invoke(e.Action); + return true; + case GlobalAction.ToggleMute: case GlobalAction.NextVolumeMeter: case GlobalAction.PreviousVolumeMeter: - ActionRequested?.Invoke(e.Action); + if (!e.Repeat) + ActionRequested?.Invoke(e.Action); return true; } From 93ffe3d7adcac60168368a458e8920c667ca26d1 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Thu, 27 Oct 2022 07:25:50 +0200 Subject: [PATCH 245/321] Revert to first approach --- .../Beatmaps/TaikoBeatmapConverter.cs | 23 +++++++++++++++++++ .../Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Visual/Editing/TestSceneTimingScreen.cs | 8 +++---- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 7 +++++- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 18 ++++++++------- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 524565a863..c1e1052569 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -57,6 +57,29 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Beatmap converted = base.ConvertBeatmap(original, cancellationToken); + if (original.BeatmapInfo.Ruleset.OnlineID == 0) + { + // Post processing step to transform standard slider velocity changes into scroll speed changes + double lastScrollSpeed = 1; + + foreach (HitObject hitObject in original.HitObjects) + { + double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity; + + if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed)) + { + EffectControlPoint currentControlPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime); + + if (Precision.AlmostEquals(currentControlPoint.Time, hitObject.StartTime)) + currentControlPoint.ScrollSpeed = nextScrollSpeed; + else + converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint { ScrollSpeed = nextScrollSpeed }); + + lastScrollSpeed = nextScrollSpeed; + } + } + } + if (original.BeatmapInfo.Ruleset.OnlineID == 3) { // Post processing step to transform mania hit objects with the same start time into strong hits diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index d9bbe7a51e..fdd0167ed3 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, controlPoints.TimingPoints.Count); Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); Assert.AreEqual(34, controlPoints.SamplePoints.Count); - Assert.AreEqual(13, controlPoints.EffectPoints.Count); + Assert.AreEqual(8, controlPoints.EffectPoints.Count); var timingPoint = controlPoints.TimingPointAt(0); Assert.AreEqual(956, timingPoint.Time); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index e93382cc9b..03c184c27d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -67,8 +67,8 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); - AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 37560); - AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 37560); + AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670); + AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 54670); AddStep("Seek to just before next point", () => EditorClock.Seek(69000)); AddStep("Start clock", () => EditorClock.Start()); @@ -85,8 +85,8 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); - AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 37560); - AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 37560); + AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 54670); + AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 54670); AddStep("Seek to later", () => EditorClock.Seek(80000)); AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 69670); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index c38510aeef..9c50c06cb7 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -448,9 +448,14 @@ namespace osu.Game.Beatmaps.Formats { KiaiMode = kiaiMode, OmitFirstBarLine = omitFirstBarSignature, - ScrollSpeed = speedMultiplier, }; + int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; + + // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. + if (onlineRulesetID == 1 || onlineRulesetID == 3) + effectPoint.ScrollSpeed = speedMultiplier; + addControlPoint(time, effectPoint, timingChange); addControlPoint(time, new LegacySampleControlPoint diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index b1383c9e85..03c63ff4f2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -10,7 +10,6 @@ using System.IO; using System.Linq; using System.Text; using JetBrains.Annotations; -using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; @@ -186,18 +185,18 @@ namespace osu.Game.Beatmaps.Formats SampleControlPoint lastRelevantSamplePoint = null; DifficultyControlPoint lastRelevantDifficultyPoint = null; + // In osu!taiko and osu!mania, a scroll speed is stored as "slider velocity" in legacy formats. + // In that case, a scrolling speed change is a global effect and per-hit object difficulty control points are ignored. + bool scrollSpeedEncodedAsSliderVelocity = onlineRulesetID == 1 || onlineRulesetID == 3; + // iterate over hitobjects and pull out all required sample and difficulty changes extractDifficultyControlPoints(beatmap.HitObjects); extractSampleControlPoints(beatmap.HitObjects); - foreach (var point in legacyControlPoints.EffectPoints) + if (scrollSpeedEncodedAsSliderVelocity) { - DifficultyControlPoint difficultyPoint = legacyControlPoints.DifficultyPointAt(point.Time); - - if (Precision.AlmostEquals(difficultyPoint.SliderVelocity, point.ScrollSpeed, acceptableDifference: point.ScrollSpeedBindable.Precision)) - continue; - - legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); + foreach (var point in legacyControlPoints.EffectPoints) + legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed }); } foreach (var group in legacyControlPoints.Groups) @@ -245,6 +244,9 @@ namespace osu.Game.Beatmaps.Formats IEnumerable collectDifficultyControlPoints(IEnumerable hitObjects) { + if (scrollSpeedEncodedAsSliderVelocity) + yield break; + foreach (var hitObject in hitObjects) yield return hitObject.DifficultyControlPoint; } From d5c88cca8c5ba9bde038cf5c5a47fab70293000f Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Thu, 27 Oct 2022 07:29:36 +0200 Subject: [PATCH 246/321] Fix result of merge conflict --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 9c50c06cb7..5f0a2a0824 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -450,8 +450,6 @@ namespace osu.Game.Beatmaps.Formats OmitFirstBarLine = omitFirstBarSignature, }; - int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID; - // osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments. if (onlineRulesetID == 1 || onlineRulesetID == 3) effectPoint.ScrollSpeed = speedMultiplier; From 4127aaa9882c3b2007849e982b9fcc1d0b551ec4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 27 Oct 2022 14:34:24 +0900 Subject: [PATCH 247/321] Extract general elements from HubClientConnector into SocketClientConnector --- osu.Game/Online/HubClient.cs | 28 ++++ osu.Game/Online/HubClientConnector.cs | 161 +------------------- osu.Game/Online/SocketClient.cs | 24 +++ osu.Game/Online/SocketClientConnector.cs | 183 +++++++++++++++++++++++ 4 files changed, 242 insertions(+), 154 deletions(-) create mode 100644 osu.Game/Online/HubClient.cs create mode 100644 osu.Game/Online/SocketClient.cs create mode 100644 osu.Game/Online/SocketClientConnector.cs diff --git a/osu.Game/Online/HubClient.cs b/osu.Game/Online/HubClient.cs new file mode 100644 index 0000000000..262e298f34 --- /dev/null +++ b/osu.Game/Online/HubClient.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; + +namespace osu.Game.Online +{ + public class HubClient : SocketClient + { + public readonly HubConnection Connection; + + public HubClient(HubConnection connection) + { + Connection = connection; + Connection.Closed += InvokeClosed; + } + + public override Task StartAsync(CancellationToken cancellationToken) => Connection.StartAsync(cancellationToken); + + public override async ValueTask DisposeAsync() + { + await base.DisposeAsync().ConfigureAwait(false); + await Connection.DisposeAsync().ConfigureAwait(false); + } + } +} diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 6bfe09e911..33e9f92817 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -10,13 +10,11 @@ using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using osu.Framework; -using osu.Framework.Bindables; -using osu.Framework.Logging; using osu.Game.Online.API; namespace osu.Game.Online { - public class HubClientConnector : IHubClientConnector + public class HubClientConnector : SocketClientConnector, IHubClientConnector { public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down."; @@ -25,7 +23,6 @@ namespace osu.Game.Online /// public Action? ConfigureConnection { get; set; } - private readonly string clientName; private readonly string endpoint; private readonly string versionHash; private readonly bool preferMessagePack; @@ -34,18 +31,7 @@ namespace osu.Game.Online /// /// The current connection opened by this connector. /// - public HubConnection? CurrentConnection { get; private set; } - - /// - /// Whether this is connected to the hub, use to access the connection, if this is true. - /// - public IBindable IsConnected => isConnected; - - private readonly Bindable isConnected = new Bindable(); - private readonly SemaphoreSlim connectionLock = new SemaphoreSlim(1); - private CancellationTokenSource connectCancelSource = new CancellationTokenSource(); - - private readonly IBindable apiState = new Bindable(); + public new HubConnection? CurrentConnection => ((HubClient?)base.CurrentConnection)?.Connection; /// /// Constructs a new . @@ -56,99 +42,16 @@ namespace osu.Game.Online /// The hash representing the current game version, used for verification purposes. /// Whether to use MessagePack for serialisation if available on this platform. public HubClientConnector(string clientName, string endpoint, IAPIProvider api, string versionHash, bool preferMessagePack = true) + : base(api) { - this.clientName = clientName; + ClientName = clientName; this.endpoint = endpoint; this.api = api; this.versionHash = versionHash; this.preferMessagePack = preferMessagePack; - - apiState.BindTo(api.State); - apiState.BindValueChanged(_ => Task.Run(connectIfPossible), true); } - public Task Reconnect() - { - Logger.Log($"{clientName} reconnecting...", LoggingTarget.Network); - return Task.Run(connectIfPossible); - } - - private async Task connectIfPossible() - { - switch (apiState.Value) - { - case APIState.Failing: - case APIState.Offline: - await disconnect(true); - break; - - case APIState.Online: - await connect(); - break; - } - } - - private async Task connect() - { - cancelExistingConnect(); - - if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false)) - throw new TimeoutException("Could not obtain a lock to connect. A previous attempt is likely stuck."); - - try - { - while (apiState.Value == APIState.Online) - { - // ensure any previous connection was disposed. - // this will also create a new cancellation token source. - await disconnect(false).ConfigureAwait(false); - - // this token will be valid for the scope of this connection. - // if cancelled, we can be sure that a disconnect or reconnect is handled elsewhere. - var cancellationToken = connectCancelSource.Token; - - cancellationToken.ThrowIfCancellationRequested(); - - Logger.Log($"{clientName} connecting...", LoggingTarget.Network); - - try - { - // importantly, rebuild the connection each attempt to get an updated access token. - CurrentConnection = buildConnection(cancellationToken); - - await CurrentConnection.StartAsync(cancellationToken).ConfigureAwait(false); - - Logger.Log($"{clientName} connected!", LoggingTarget.Network); - isConnected.Value = true; - return; - } - catch (OperationCanceledException) - { - //connection process was cancelled. - throw; - } - catch (Exception e) - { - await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false); - } - } - } - finally - { - connectionLock.Release(); - } - } - - /// - /// Handles an exception and delays an async flow. - /// - private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken) - { - Logger.Log($"{clientName} connect attempt failed: {exception.Message}", LoggingTarget.Network); - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); - } - - private HubConnection buildConnection(CancellationToken cancellationToken) + protected override Task BuildConnectionAsync(CancellationToken cancellationToken) { var builder = new HubConnectionBuilder() .WithUrl(endpoint, options => @@ -188,59 +91,9 @@ namespace osu.Game.Online ConfigureConnection?.Invoke(newConnection); - newConnection.Closed += ex => onConnectionClosed(ex, cancellationToken); - return newConnection; + return Task.FromResult((SocketClient)new HubClient(newConnection)); } - private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) - { - isConnected.Value = false; - - if (ex != null) - await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false); - else - Logger.Log($"{clientName} disconnected", LoggingTarget.Network); - - // make sure a disconnect wasn't triggered (and this is still the active connection). - if (!cancellationToken.IsCancellationRequested) - await Task.Run(connect, default).ConfigureAwait(false); - } - - private async Task disconnect(bool takeLock) - { - cancelExistingConnect(); - - if (takeLock) - { - if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false)) - throw new TimeoutException("Could not obtain a lock to disconnect. A previous attempt is likely stuck."); - } - - try - { - if (CurrentConnection != null) - await CurrentConnection.DisposeAsync().ConfigureAwait(false); - } - finally - { - CurrentConnection = null; - if (takeLock) - connectionLock.Release(); - } - } - - private void cancelExistingConnect() - { - connectCancelSource.Cancel(); - connectCancelSource = new CancellationTokenSource(); - } - - public override string ToString() => $"Connector for {clientName} ({(IsConnected.Value ? "connected" : "not connected")}"; - - public void Dispose() - { - apiState.UnbindAll(); - cancelExistingConnect(); - } + protected override string ClientName { get; } } } diff --git a/osu.Game/Online/SocketClient.cs b/osu.Game/Online/SocketClient.cs new file mode 100644 index 0000000000..3b4aa1b49b --- /dev/null +++ b/osu.Game/Online/SocketClient.cs @@ -0,0 +1,24 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace osu.Game.Online +{ + public abstract class SocketClient : IAsyncDisposable + { + public event Func? Closed; + + protected Task InvokeClosed(Exception? exception) => Closed?.Invoke(exception) ?? Task.CompletedTask; + + public abstract Task StartAsync(CancellationToken cancellationToken); + + public virtual ValueTask DisposeAsync() + { + Closed = null; + return new ValueTask(Task.CompletedTask); + } + } +} diff --git a/osu.Game/Online/SocketClientConnector.cs b/osu.Game/Online/SocketClientConnector.cs new file mode 100644 index 0000000000..823e724ef9 --- /dev/null +++ b/osu.Game/Online/SocketClientConnector.cs @@ -0,0 +1,183 @@ +// 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.Threading; +using System.Threading.Tasks; +using osu.Framework.Bindables; +using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Online.API; + +namespace osu.Game.Online +{ + public abstract class SocketClientConnector : Component + { + /// + /// Whether this is connected to the hub, use to access the connection, if this is true. + /// + public IBindable IsConnected => isConnected; + + /// + /// The current connection opened by this connector. + /// + public SocketClient? CurrentConnection { get; private set; } + + private readonly Bindable isConnected = new Bindable(); + private readonly SemaphoreSlim connectionLock = new SemaphoreSlim(1); + private CancellationTokenSource connectCancelSource = new CancellationTokenSource(); + + private readonly IBindable apiState = new Bindable(); + + /// + /// Constructs a new . + /// + /// An API provider used to react to connection state changes. + protected SocketClientConnector(IAPIProvider api) + { + apiState.BindTo(api.State); + apiState.BindValueChanged(_ => Task.Run(connectIfPossible), true); + } + + public Task Reconnect() + { + Logger.Log($"{ClientName} reconnecting...", LoggingTarget.Network); + return Task.Run(connectIfPossible); + } + + private async Task connectIfPossible() + { + switch (apiState.Value) + { + case APIState.Failing: + case APIState.Offline: + await disconnect(true); + break; + + case APIState.Online: + await connect(); + break; + } + } + + private async Task connect() + { + cancelExistingConnect(); + + if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false)) + throw new TimeoutException("Could not obtain a lock to connect. A previous attempt is likely stuck."); + + try + { + while (apiState.Value == APIState.Online) + { + // ensure any previous connection was disposed. + // this will also create a new cancellation token source. + await disconnect(false).ConfigureAwait(false); + + // this token will be valid for the scope of this connection. + // if cancelled, we can be sure that a disconnect or reconnect is handled elsewhere. + var cancellationToken = connectCancelSource.Token; + + cancellationToken.ThrowIfCancellationRequested(); + + Logger.Log($"{ClientName} connecting...", LoggingTarget.Network); + + try + { + // importantly, rebuild the connection each attempt to get an updated access token. + CurrentConnection = await BuildConnectionAsync(cancellationToken).ConfigureAwait(false); + CurrentConnection.Closed += ex => onConnectionClosed(ex, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + await CurrentConnection.StartAsync(cancellationToken).ConfigureAwait(false); + + Logger.Log($"{ClientName} connected!", LoggingTarget.Network); + isConnected.Value = true; + return; + } + catch (OperationCanceledException) + { + //connection process was cancelled. + throw; + } + catch (Exception e) + { + await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false); + } + } + } + finally + { + connectionLock.Release(); + } + } + + /// + /// Handles an exception and delays an async flow. + /// + private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken) + { + Logger.Log($"{ClientName} connect attempt failed: {exception.Message}", LoggingTarget.Network); + await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + } + + protected abstract Task BuildConnectionAsync(CancellationToken cancellationToken); + + private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) + { + isConnected.Value = false; + + if (ex != null) + await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false); + else + Logger.Log($"{ClientName} disconnected", LoggingTarget.Network); + + // make sure a disconnect wasn't triggered (and this is still the active connection). + if (!cancellationToken.IsCancellationRequested) + await Task.Run(connect, default).ConfigureAwait(false); + } + + private async Task disconnect(bool takeLock) + { + cancelExistingConnect(); + + if (takeLock) + { + if (!await connectionLock.WaitAsync(10000).ConfigureAwait(false)) + throw new TimeoutException("Could not obtain a lock to disconnect. A previous attempt is likely stuck."); + } + + try + { + if (CurrentConnection != null) + await CurrentConnection.DisposeAsync().ConfigureAwait(false); + } + finally + { + CurrentConnection = null; + if (takeLock) + connectionLock.Release(); + } + } + + private void cancelExistingConnect() + { + connectCancelSource.Cancel(); + connectCancelSource = new CancellationTokenSource(); + } + + protected virtual string ClientName => GetType().ReadableName(); + + public override string ToString() => $"{ClientName} ({(IsConnected.Value ? "connected" : "not connected")})"; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + apiState.UnbindAll(); + cancelExistingConnect(); + } + } +} From 097d310d7464056893d2baba9c1a23a3dac0ccfa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 14:40:14 +0900 Subject: [PATCH 248/321] Only draw path visualiser when hovered or single slider is selected --- .../Sliders/SliderSelectionBlueprint.cs | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 265a1d21b1..9716f2820f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -59,6 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private readonly BindableList controlPoints = new BindableList(); private readonly IBindable pathVersion = new Bindable(); + private BindableList selectedObjects; public SliderSelectionBlueprint(Slider slider) : base(slider) @@ -86,6 +87,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders pathVersion.BindValueChanged(_ => editorBeatmap?.Update(HitObject)); BodyPiece.UpdateFrom(HitObject); + + selectedObjects = editorBeatmap.SelectedHitObjects.GetBoundCopy(); + selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition()); } public override bool HandleQuickDeletion() @@ -108,14 +112,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BodyPiece.UpdateFrom(HitObject); } + protected override bool OnHover(HoverEvent e) + { + updateVisualDefinition(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateVisualDefinition(); + base.OnHoverLost(e); + } + protected override void OnSelected() { - AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true) - { - RemoveControlPointsRequested = removeControlPoints, - SplitControlPointsRequested = splitControlPoints - }); - + updateVisualDefinition(); base.OnSelected(); } @@ -123,13 +134,33 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { base.OnDeselected(); - // throw away frame buffers on deselection. - ControlPointVisualiser?.Expire(); - ControlPointVisualiser = null; - + updateVisualDefinition(); BodyPiece.RecyclePath(); } + private void updateVisualDefinition() + { + bool hasSingleObjectSelected = editorBeatmap.SelectedHitObjects.Count == 1; + + // To reduce overhead of drawing these blueprints, only add extra detail when hovered or when only this slider is selected. + if (IsSelected && (hasSingleObjectSelected || IsHovered)) + { + if (ControlPointVisualiser == null) + { + AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true) + { + RemoveControlPointsRequested = removeControlPoints, + SplitControlPointsRequested = splitControlPoints + }); + } + } + else + { + ControlPointVisualiser?.Expire(); + ControlPointVisualiser = null; + } + } + private Vector2 rightClickPosition; protected override bool OnMouseDown(MouseDownEvent e) From 2f172b66cf80290ee3b9bfd3d4c8d971f342d05c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 14:52:46 +0900 Subject: [PATCH 249/321] Block hover if a multi-selection is made to avoid overlapping path visualisers --- .../Blueprints/Sliders/SliderSelectionBlueprint.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 9716f2820f..ab164028b1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -104,6 +104,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } + private bool hasSingleObjectSelected => editorBeatmap.SelectedHitObjects.Count == 1; + protected override void Update() { base.Update(); @@ -115,7 +117,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnHover(HoverEvent e) { updateVisualDefinition(); - return base.OnHover(e); + + // In the case more than a single object is selected, block hover from arriving at sliders behind this one. + // Without doing this, the path visualisers of potentially hundreds of sliders will render, which is not only + // visually noisy but also functionally useless. + return !hasSingleObjectSelected; } protected override void OnHoverLost(HoverLostEvent e) @@ -140,8 +146,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateVisualDefinition() { - bool hasSingleObjectSelected = editorBeatmap.SelectedHitObjects.Count == 1; - // To reduce overhead of drawing these blueprints, only add extra detail when hovered or when only this slider is selected. if (IsSelected && (hasSingleObjectSelected || IsHovered)) { From 0c7672e1248b190686a2d69b58e9f3612f1884e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 16:02:04 +0900 Subject: [PATCH 250/321] Avoid unnecessarily refreshing `SliderBodyPiece`'s path --- .../Sliders/Components/SliderBodyPiece.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index bb967a0a76..da2a6ced67 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -40,16 +40,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components body.BorderColour = colours.Yellow; } + private int? lastVersion; + public override void UpdateFrom(Slider hitObject) { base.UpdateFrom(hitObject); body.PathRadius = hitObject.Scale * OsuHitObject.OBJECT_RADIUS; - var vertices = new List(); - hitObject.Path.GetPathToProgress(vertices, 0, 1); + if (lastVersion != hitObject.Path.Version.Value) + { + lastVersion = hitObject.Path.Version.Value; - body.SetVertices(vertices); + var vertices = new List(); + hitObject.Path.GetPathToProgress(vertices, 0, 1); + + body.SetVertices(vertices); + } Size = body.Size; OriginPosition = body.PathOffset; From 39e0362db13a96bed21f265bcb5ae94c87e28097 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 16:15:22 +0900 Subject: [PATCH 251/321] Reduce size of spinner blueprint to better represent gameplay size --- .../Spinners/Components/SpinnerPiece.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs index 28690ee0b7..2b1bb5185c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs @@ -16,8 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components { public class SpinnerPiece : BlueprintPiece { - private readonly CircularContainer circle; - private readonly RingPiece ring; + private readonly Circle circle; + private readonly Circle ring; public SpinnerPiece() { @@ -25,18 +25,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components RelativeSizeAxes = Axes.Both; FillMode = FillMode.Fit; - Size = new Vector2(1.3f); + Size = new Vector2(1); InternalChildren = new Drawable[] { - circle = new CircularContainer + circle = new Circle { RelativeSizeAxes = Axes.Both, - Masking = true, Alpha = 0.5f, - Child = new Box { RelativeSizeAxes = Axes.Both } }, - ring = new RingPiece() + ring = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(OsuHitObject.OBJECT_RADIUS), + }, }; } From 68d1febe6e9b5939f1f32a112b4c81f1567b1771 Mon Sep 17 00:00:00 2001 From: nullium21 Date: Thu, 27 Oct 2022 10:33:50 +0300 Subject: [PATCH 252/321] Use a regex for matching invalid characters instead What the regex does is it matches everything except alphanumeric and [_()[] -], excluding EOL since regex101 seems to also match it, and we probably don't want that to happen. --- osu.Game/Extensions/ModelExtensions.cs | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 51bea02726..8acc079805 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using System.Text.RegularExpressions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO; @@ -136,34 +137,14 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - private static bool isValidFilenameChar(this char ch) - { - if (ch >= '0' && ch <= '9') - return true; - - if (ch >= 'A' && ch <= 'Z') - return true; - - if (ch >= 'a' && ch <= 'z') - return true; - - return "-_()[]".Contains(ch); - } + private static Regex invalid_filename_chars = new(@"(?!$)[^A-Za-z0-9_()[\] -]", RegexOptions.Compiled); /// /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. /// public static string GetValidArchiveContentFilename(this string filename) { - char[] resultData = filename.ToCharArray(); - - for (int i = 0; i < resultData.Length; i++) - { - if (!isValidFilenameChar(resultData[i])) - resultData[i] = '_'; - } - - return new string(resultData); + return invalid_filename_chars.Replace(filename, "_"); } } } From d48f95cf7c0f317c6b532e9d455b592e9a521fb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 13:16:15 +0900 Subject: [PATCH 253/321] Use shader based implementation instead of sprites --- .../Skinning/Argon/ArgonDropletPiece.cs | 37 ++++++++++--------- .../Skinning/Argon/ArgonFruitPiece.cs | 36 ++++++++++-------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs index c607b96729..63a41edaf7 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs @@ -5,8 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Catch.UI; @@ -25,11 +24,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon private float rotationRandomness; [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load() { RelativeSizeAxes = Axes.Both; - const float droplet_scale_down = 0.5f; + const float droplet_scale_down = 0.7f; + + int largeBlobSeed = RNG.Next(); InternalChildren = new[] { @@ -47,38 +48,42 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Sprite + new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, + InnerRadius = 0.5f, Alpha = 0.15f, - Texture = getTexture("A") + Seed = largeBlobSeed }, - new Sprite + new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, + InnerRadius = 0.4f, Alpha = 0.5f, - Scale = new Vector2(0.8f), - Texture = getTexture("A") + Scale = new Vector2(0.7f), + Seed = RNG.Next() }, } }, - hyperBorderPiece = new Sprite + hyperBorderPiece = new CircularBlob { Scale = new Vector2(droplet_scale_down), Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, + InnerRadius = 0.5f, Alpha = 0.15f, - Texture = getTexture("A"), + Seed = largeBlobSeed }, }; - - Texture getTexture(string type) => textures.Get($"Gameplay/catch/blob-{type}{RNG.Next(1, 7)}"); } protected override void LoadComplete() @@ -106,10 +111,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon for (int i = 0; i < layers.Count; i++) { layers[i].Rotation -= - // Layers are ordered from largest to smallest. Smaller layers should rotate more. - (i * 4) - * (float)Clock.ElapsedFrameTime - * 0.2f * rotationRandomness + (float)Clock.ElapsedFrameTime + * 0.4f * rotationRandomness // Each layer should alternate rotation direction. * (i % 2 == 1 ? 0.5f : 1); } diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs index 2412de7dbe..28538d48b3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs @@ -5,8 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Catch.UI; @@ -25,11 +24,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon private float rotationRandomness; [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load() { RelativeSizeAxes = Axes.Both; - Texture largeTexture = getTexture("A"); + int largeBlobSeed = RNG.Next(); InternalChildren = new[] { @@ -44,43 +43,50 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Sprite + new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Alpha = 0.15f, - Texture = largeTexture + InnerRadius = 0.5f, + Size = new Vector2(1.1f), + Seed = largeBlobSeed, }, - new Sprite + new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, + InnerRadius = 0.2f, Alpha = 0.5f, - Texture = getTexture("B") + Seed = RNG.Next(), }, - new Sprite + new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, - Texture = getTexture("C") + InnerRadius = 0.05f, + Seed = RNG.Next(), }, } }, - hyperBorderPiece = new Sprite + hyperBorderPiece = new CircularBlob { Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, + RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, - Alpha = 0.15f, - Texture = largeTexture, + InnerRadius = 0.08f, + Size = new Vector2(1.15f), + Seed = largeBlobSeed }, }; - - Texture getTexture(string type) => textures.Get($"Gameplay/catch/blob-{type}{RNG.Next(1, 7)}"); } protected override void LoadComplete() From 6fe8298152db01184197632997a2c0247fd90ac0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 19:02:01 +0900 Subject: [PATCH 254/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f251e8ee71..5d6c32cea8 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 22474c0592..5d1a834b0e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index cf70b65578..bf218f856a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From 3c4ba6e9fcd596a6fa0d894c47d8ee5d92f073bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 19:02:32 +0900 Subject: [PATCH 255/321] Fix `SoloScoreInfo.Rank` not being serialised if rank is `D` --- osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index 4469d50acb..2095b2c700 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -45,6 +46,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("rank")] + // ScoreRank is aligned to make 0 equal D. We still want to serialise this (even when DefaultValueHandling.Ignore is used), + // so set the default to an impossible value. + [DefaultValue(null)] public ScoreRank Rank { get; set; } [JsonProperty("started_at")] From 361153f598c34072b009cd91860dc2470917925a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Oct 2022 19:05:50 +0900 Subject: [PATCH 256/321] Fix nullref in tests due to missing `EditorBeatmap` dependency --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index ab164028b1..0b0a76cce0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } - private bool hasSingleObjectSelected => editorBeatmap.SelectedHitObjects.Count == 1; + private bool hasSingleObjectSelected => editorBeatmap == null || editorBeatmap.SelectedHitObjects.Count == 1; protected override void Update() { From e582d9d5a8720928e03a792a9b92dc91fbd54baf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 01:17:09 +0900 Subject: [PATCH 257/321] Remove unused using statements --- .../Edit/Blueprints/Spinners/Components/SpinnerPiece.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs index 2b1bb5185c..b5a13a22ce 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs @@ -5,11 +5,9 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Skinning.Default; using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components From 7faf5cd1f6f7962002162dafc463037e0e2011e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 01:20:17 +0900 Subject: [PATCH 258/321] Fix one more null edge case --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 0b0a76cce0..b5b04823a7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private readonly BindableList controlPoints = new BindableList(); private readonly IBindable pathVersion = new Bindable(); - private BindableList selectedObjects; + private readonly BindableList selectedObjects = new BindableList(); public SliderSelectionBlueprint(Slider slider) : base(slider) @@ -88,7 +88,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BodyPiece.UpdateFrom(HitObject); - selectedObjects = editorBeatmap.SelectedHitObjects.GetBoundCopy(); + if (editorBeatmap != null) + selectedObjects.BindTo(editorBeatmap.SelectedHitObjects); selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition()); } @@ -104,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } - private bool hasSingleObjectSelected => editorBeatmap == null || editorBeatmap.SelectedHitObjects.Count == 1; + private bool hasSingleObjectSelected => editorBeatmap == null || selectedObjects.Count == 1; protected override void Update() { From dcff8a193cf1f206d614cb59e8092801b0d050c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 01:26:10 +0900 Subject: [PATCH 259/321] Use better method of forcing serialisation --- osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index 2095b2c700..57586e3d77 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -45,10 +44,8 @@ namespace osu.Game.Online.API.Requests.Responses public int MaxCombo { get; set; } [JsonConverter(typeof(StringEnumConverter))] - [JsonProperty("rank")] - // ScoreRank is aligned to make 0 equal D. We still want to serialise this (even when DefaultValueHandling.Ignore is used), - // so set the default to an impossible value. - [DefaultValue(null)] + // ScoreRank is aligned to make 0 equal D. We still want to serialise this (even when DefaultValueHandling.Ignore is used). + [JsonProperty("rank", DefaultValueHandling = DefaultValueHandling.Include)] public ScoreRank Rank { get; set; } [JsonProperty("started_at")] From 446a9c1b05d5c98e89d85c699afa82338f9f7810 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Thu, 27 Oct 2022 23:41:17 +0200 Subject: [PATCH 260/321] Make added effect points inherit previous effect point settings --- .../Beatmaps/TaikoBeatmapConverter.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index c1e1052569..3cc47deed0 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -65,17 +65,16 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps foreach (HitObject hitObject in original.HitObjects) { double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity; + EffectControlPoint currentEffectPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime); - if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed)) + if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed, acceptableDifference: currentEffectPoint.ScrollSpeedBindable.Precision)) { - EffectControlPoint currentControlPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime); - - if (Precision.AlmostEquals(currentControlPoint.Time, hitObject.StartTime)) - currentControlPoint.ScrollSpeed = nextScrollSpeed; - else - converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint { ScrollSpeed = nextScrollSpeed }); - - lastScrollSpeed = nextScrollSpeed; + converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint + { + KiaiMode = currentEffectPoint.KiaiMode, + OmitFirstBarLine = currentEffectPoint.OmitFirstBarLine, + ScrollSpeed = lastScrollSpeed = nextScrollSpeed, + }); } } } From 9969c00ead98ccd88a3eb5f88d7c2357628a495c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 10:19:53 +0900 Subject: [PATCH 261/321] Apply two minor fixes from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs | 2 +- .../Skinning/Argon/CatchArgonSkinTransformer.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs index 63a41edaf7..267f8a06a3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon layers[i].Rotation -= (float)Clock.ElapsedFrameTime * 0.4f * rotationRandomness - // Each layer should alternate rotation direction. + // Each layer should alternate rotation speed. * (i % 2 == 1 ? 0.5f : 1); } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs index 30542187f2..8dae0a2b78 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/CatchArgonSkinTransformer.cs @@ -17,9 +17,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { switch (component) { - case CatchSkinComponent osuComponent: + case CatchSkinComponent catchComponent: // TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries. - switch (osuComponent.Component) + switch (catchComponent.Component) { case CatchSkinComponents.HitExplosion: return new ArgonHitExplosion(); From 71970d83cf658add8dbe68cf15390da4f305d489 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 12:47:44 +0900 Subject: [PATCH 262/321] Remove obsoleted `ShowProgressGraph` global setting This was moved to the component itself. --- osu.Game/Configuration/OsuConfigManager.cs | 3 -- .../Screens/Play/HUD/DefaultSongProgress.cs | 48 ------------------- 2 files changed, 51 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 1378e1691a..825c345c0a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -127,7 +127,6 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); - SetDefault(OsuSetting.ShowProgressGraph, true); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); @@ -302,8 +301,6 @@ namespace osu.Game.Configuration FloatingComments, HUDVisibilityMode, - // This has been migrated to the component itself. can be removed 20221027. - ShowProgressGraph, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, MouseDisableButtons, diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs index 30b420441c..45d0cf8462 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgress.cs @@ -9,7 +9,6 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -45,12 +44,6 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private DrawableRuleset? drawableRuleset { get; set; } - [Resolved] - private OsuConfigManager config { get; set; } = null!; - - [Resolved] - private SkinManager skinManager { get; set; } = null!; - public DefaultSongProgress() { RelativeSizeAxes = Axes.X; @@ -100,47 +93,6 @@ namespace osu.Game.Screens.Play.HUD { AllowSeeking.BindValueChanged(_ => updateBarVisibility(), true); ShowGraph.BindValueChanged(_ => updateGraphVisibility(), true); - - migrateSettingFromConfig(); - } - - /// - /// This setting has been migrated to a per-component level. - /// Only take the value from the config if it is in a non-default state (then reset it to default so it only applies once). - /// - /// Can be removed 20221027. - /// - private void migrateSettingFromConfig() - { - Bindable configShowGraph = config.GetBindable(OsuSetting.ShowProgressGraph); - - if (!configShowGraph.IsDefault) - { - ShowGraph.Value = configShowGraph.Value; - - // This is pretty ugly, but the only way to make this stick... - var skinnableTarget = this.FindClosestParent(); - - if (skinnableTarget != null) - { - // If the skin is not mutable, a mutable instance will be created, causing this migration logic to run again on the correct skin. - // Therefore we want to avoid resetting the config value on this invocation. - if (skinManager.EnsureMutableSkin()) - return; - - // If `EnsureMutableSkin` actually changed the skin, default layout may take a frame to apply. - // See `SkinnableTargetComponentsContainer`'s use of ScheduleAfterChildren. - ScheduleAfterChildren(() => - { - var skin = skinManager.CurrentSkin.Value; - skin.UpdateDrawableTarget(skinnableTarget); - - skinManager.Save(skin); - }); - - configShowGraph.SetDefault(); - } - } } protected override void PopIn() From 29f504ecc031e8f930e639da5c8d4d7197fdc986 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 13:00:39 +0900 Subject: [PATCH 263/321] Remove obsoleted tournament migration flow --- .../NonVisual/CustomTourneyDirectoryTest.cs | 79 +----------------- ...eManager.cs => TournamentConfigManager.cs} | 13 ++- osu.Game.Tournament/IO/TournamentStorage.cs | 80 ++----------------- 3 files changed, 19 insertions(+), 153 deletions(-) rename osu.Game.Tournament/Configuration/{TournamentStorageManager.cs => TournamentConfigManager.cs} (55%) diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index d314f40c30..f778e67f58 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tournament.Tests.NonVisual // manual cleaning so we can prepare a config file. storage.DeleteDirectory(string.Empty); - using (var storageConfig = new TournamentStorageManager(storage)) + using (var storageConfig = new TournamentConfigManager(storage)) storageConfig.SetValue(StorageConfig.CurrentTournament, custom_tournament); try @@ -66,82 +66,5 @@ namespace osu.Game.Tournament.Tests.NonVisual } } } - - [Test] - public void TestMigration() - { - using (HeadlessGameHost host = new TestRunHeadlessGameHost(nameof(TestMigration), null)) // don't use clean run as we are writing test files for migration. - { - string osuRoot = Path.Combine(host.UserStoragePaths.First(), nameof(TestMigration)); - string configFile = Path.Combine(osuRoot, "tournament.ini"); - - if (File.Exists(configFile)) - File.Delete(configFile); - - // Recreate the old setup that uses "tournament" as the base path. - string oldPath = Path.Combine(osuRoot, "tournament"); - - string videosPath = Path.Combine(oldPath, "Videos"); - string modsPath = Path.Combine(oldPath, "Mods"); - string flagsPath = Path.Combine(oldPath, "Flags"); - - Directory.CreateDirectory(videosPath); - Directory.CreateDirectory(modsPath); - Directory.CreateDirectory(flagsPath); - - // Define testing files corresponding to the specific file migrations that are needed - string bracketFile = Path.Combine(osuRoot, TournamentGameBase.BRACKET_FILENAME); - - string drawingsConfig = Path.Combine(osuRoot, "drawings.ini"); - string drawingsFile = Path.Combine(osuRoot, "drawings.txt"); - string drawingsResult = Path.Combine(osuRoot, "drawings_results.txt"); - - // Define sample files to test recursive copying - string videoFile = Path.Combine(videosPath, "video.mp4"); - string modFile = Path.Combine(modsPath, "mod.png"); - string flagFile = Path.Combine(flagsPath, "flag.png"); - - File.WriteAllText(bracketFile, "{}"); - File.WriteAllText(drawingsConfig, "test"); - File.WriteAllText(drawingsFile, "test"); - File.WriteAllText(drawingsResult, "test"); - File.WriteAllText(videoFile, "test"); - File.WriteAllText(modFile, "test"); - File.WriteAllText(flagFile, "test"); - - try - { - var osu = LoadTournament(host); - - var storage = osu.Dependencies.Get(); - - string migratedPath = Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default"); - - videosPath = Path.Combine(migratedPath, "Videos"); - modsPath = Path.Combine(migratedPath, "Mods"); - flagsPath = Path.Combine(migratedPath, "Flags"); - - videoFile = Path.Combine(videosPath, "video.mp4"); - modFile = Path.Combine(modsPath, "mod.png"); - flagFile = Path.Combine(flagsPath, "flag.png"); - - Assert.That(storage.GetFullPath("."), Is.EqualTo(migratedPath)); - - Assert.True(storage.Exists(TournamentGameBase.BRACKET_FILENAME)); - Assert.True(storage.Exists("drawings.txt")); - Assert.True(storage.Exists("drawings_results.txt")); - - Assert.True(storage.Exists("drawings.ini")); - - Assert.True(storage.Exists(videoFile)); - Assert.True(storage.Exists(modFile)); - Assert.True(storage.Exists(flagFile)); - } - finally - { - host.Exit(); - } - } - } } } diff --git a/osu.Game.Tournament/Configuration/TournamentStorageManager.cs b/osu.Game.Tournament/Configuration/TournamentConfigManager.cs similarity index 55% rename from osu.Game.Tournament/Configuration/TournamentStorageManager.cs rename to osu.Game.Tournament/Configuration/TournamentConfigManager.cs index 0b9a556296..8f256ba9c3 100644 --- a/osu.Game.Tournament/Configuration/TournamentStorageManager.cs +++ b/osu.Game.Tournament/Configuration/TournamentConfigManager.cs @@ -8,14 +8,23 @@ using osu.Framework.Platform; namespace osu.Game.Tournament.Configuration { - public class TournamentStorageManager : IniConfigManager + public class TournamentConfigManager : IniConfigManager { protected override string Filename => "tournament.ini"; - public TournamentStorageManager(Storage storage) + private const string default_tournament = "default"; + + public TournamentConfigManager(Storage storage) : base(storage) { } + + protected override void InitialiseDefaults() + { + base.InitialiseDefaults(); + + SetDefault(StorageConfig.CurrentTournament, default_tournament); + } } public enum StorageConfig diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index bd52b6dfed..e59f90a45e 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; -using System.IO; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; @@ -13,35 +10,28 @@ using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.IO { - public class TournamentStorage : MigratableStorage + public class TournamentStorage : WrappedStorage { - private const string default_tournament = "default"; - private readonly Storage storage; - /// /// The storage where all tournaments are located. /// public readonly Storage AllTournaments; - private readonly TournamentStorageManager storageConfig; public readonly Bindable CurrentTournament; + protected TournamentConfigManager TournamentConfigManager { get; } + public TournamentStorage(Storage storage) : base(storage.GetStorageForDirectory("tournaments"), string.Empty) { - this.storage = storage; AllTournaments = UnderlyingStorage; - storageConfig = new TournamentStorageManager(storage); + TournamentConfigManager = new TournamentConfigManager(storage); - if (storage.Exists("tournament.ini")) - { - ChangeTargetStorage(AllTournaments.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); - } - else - Migrate(AllTournaments.GetStorageForDirectory(default_tournament)); + CurrentTournament = TournamentConfigManager.GetBindable(StorageConfig.CurrentTournament); + + ChangeTargetStorage(AllTournaments.GetStorageForDirectory(CurrentTournament.Value)); - CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); CurrentTournament.BindValueChanged(updateTournament); @@ -53,62 +43,6 @@ namespace osu.Game.Tournament.IO Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); } - protected override void ChangeTargetStorage(Storage newStorage) - { - // due to an unfortunate oversight, on OSes that are sensitive to pathname casing - // the custom flags directory needed to be named `Flags` (uppercase), - // while custom mods and videos directories needed to be named `mods` and `videos` respectively (lowercase). - // to unify handling to uppercase, move any non-compliant directories automatically for the user to migrate. - // can be removed 20220528 - if (newStorage.ExistsDirectory("flags")) - AttemptOperation(() => Directory.Move(newStorage.GetFullPath("flags"), newStorage.GetFullPath("Flags"))); - if (newStorage.ExistsDirectory("mods")) - AttemptOperation(() => Directory.Move(newStorage.GetFullPath("mods"), newStorage.GetFullPath("Mods"))); - if (newStorage.ExistsDirectory("videos")) - AttemptOperation(() => Directory.Move(newStorage.GetFullPath("videos"), newStorage.GetFullPath("Videos"))); - - base.ChangeTargetStorage(newStorage); - } - public IEnumerable ListTournaments() => AllTournaments.GetDirectories(string.Empty); - - public override bool Migrate(Storage newStorage) - { - // this migration only happens once on moving to the per-tournament storage system. - // listed files are those known at that point in time. - // this can be removed at some point in the future (6 months obsoletion would mean 2021-04-19) - - var source = new DirectoryInfo(storage.GetFullPath("tournament")); - var destination = new DirectoryInfo(newStorage.GetFullPath(".")); - - if (source.Exists) - { - Logger.Log("Migrating tournament assets to default tournament storage."); - CopyRecursive(source, destination); - DeleteRecursive(source); - } - - moveFileIfExists(TournamentGameBase.BRACKET_FILENAME, destination); - moveFileIfExists("drawings.txt", destination); - moveFileIfExists("drawings_results.txt", destination); - moveFileIfExists("drawings.ini", destination); - - ChangeTargetStorage(newStorage); - storageConfig.SetValue(StorageConfig.CurrentTournament, default_tournament); - storageConfig.Save(); - - return true; - } - - private void moveFileIfExists(string file, DirectoryInfo destination) - { - if (!storage.Exists(file)) - return; - - Logger.Log($"Migrating {file} to default tournament storage."); - var fileInfo = new System.IO.FileInfo(storage.GetFullPath(file)); - AttemptOperation(() => fileInfo.CopyTo(Path.Combine(destination.FullName, fileInfo.Name), true)); - fileInfo.Delete(); - } } } From 08832ae593b558c764af7bbfc5de636a965b1e34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 13:09:28 +0900 Subject: [PATCH 264/321] Remove obsoleted `PositionalHitsounds` global setting --- osu.Game/Configuration/OsuConfigManager.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 825c345c0a..80034744de 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -118,7 +118,6 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.Prefer24HourTime, CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains(@"tt")); // Gameplay - SetDefault(OsuSetting.PositionalHitsounds, true); // replaced by level setting below, can be removed 20220703. SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1); SetDefault(OsuSetting.DimLevel, 0.7, 0, 1, 0.01); SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01); @@ -202,14 +201,11 @@ namespace osu.Game.Configuration if (!int.TryParse(pieces[0], out int year)) return; if (!int.TryParse(pieces[1], out int monthDay)) return; + // ReSharper disable once UnusedVariable int combined = (year * 10000) + monthDay; - if (combined < 20220103) - { - var positionalHitsoundsEnabled = GetBindable(OsuSetting.PositionalHitsounds); - if (!positionalHitsoundsEnabled.Value) - SetValue(OsuSetting.PositionalHitsoundsLevel, 0); - } + // migrations can be added here using a condition like: + // if (combined < 20220103) { performMigration() } } public override TrackedSettings CreateTrackedSettings() @@ -295,7 +291,6 @@ namespace osu.Game.Configuration ShowStoryboard, KeyOverlay, GameplayLeaderboard, - PositionalHitsounds, PositionalHitsoundsLevel, AlwaysPlayFirstComboBreak, FloatingComments, From 21f8a91efbd0bcd065e430314f065ab5e21ea16c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 13:12:18 +0900 Subject: [PATCH 265/321] Remove obsoleted `int` parsing logic of configuration ruleset --- osu.Game/OsuGame.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2bdcb57f2a..0a5dd02cbd 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -280,10 +280,7 @@ namespace osu.Game configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); uiScale = LocalConfig.GetBindable(OsuSetting.UIScale); - var preferredRuleset = int.TryParse(configRuleset.Value, out int rulesetId) - // int parsing can be removed 20220522 - ? RulesetStore.GetRuleset(rulesetId) - : RulesetStore.GetRuleset(configRuleset.Value); + var preferredRuleset = RulesetStore.GetRuleset(configRuleset.Value); try { From 42eafe318c132813b1abfbeac8126704fdd2d4fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 13:07:47 +0900 Subject: [PATCH 266/321] Remove various simple obsoletions --- osu.Game/Beatmaps/BeatmapInfo.cs | 8 --- osu.Game/Beatmaps/Formats/IHasComboColours.cs | 7 --- osu.Game/Beatmaps/Timing/TimeSignatures.cs | 20 -------- osu.Game/Configuration/DatabasedSetting.cs | 51 ------------------- osu.Game/Database/RealmAccess.cs | 12 +---- .../Mods/IApplicableToDrawableHitObjects.cs | 18 ------- osu.Game/Rulesets/Mods/ICreateReplay.cs | 22 -------- osu.Game/Rulesets/Mods/Mod.cs | 3 -- osu.Game/Rulesets/Mods/ModAutoplay.cs | 13 +---- .../Objects/Drawables/DrawableHitObject.cs | 12 ----- .../Ranking/Statistics/StatisticItem.cs | 6 --- osu.Game/Skinning/SkinConfiguration.cs | 2 - osu.Game/Skinning/SkinImporter.cs | 49 ------------------ 13 files changed, 2 insertions(+), 221 deletions(-) delete mode 100644 osu.Game/Beatmaps/Timing/TimeSignatures.cs delete mode 100644 osu.Game/Configuration/DatabasedSetting.cs delete mode 100644 osu.Game/Rulesets/Mods/IApplicableToDrawableHitObjects.cs delete mode 100644 osu.Game/Rulesets/Mods/ICreateReplay.cs diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 6f9df1ba7f..3208598f56 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -238,14 +238,6 @@ namespace osu.Game.Beatmaps #region Compatibility properties - [Ignored] - [Obsolete("Use BeatmapInfo.Difficulty instead.")] // can be removed 20220719 - public BeatmapDifficulty BaseDifficulty - { - get => Difficulty; - set => Difficulty = value; - } - [Ignored] public string? Path => File?.Filename; diff --git a/osu.Game/Beatmaps/Formats/IHasComboColours.cs b/osu.Game/Beatmaps/Formats/IHasComboColours.cs index d5e96da246..1d9cc0be65 100644 --- a/osu.Game/Beatmaps/Formats/IHasComboColours.cs +++ b/osu.Game/Beatmaps/Formats/IHasComboColours.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Collections.Generic; using osuTK.Graphics; @@ -22,11 +21,5 @@ namespace osu.Game.Beatmaps.Formats /// if empty, will fall back to default combo colours. /// List CustomComboColours { get; } - - /// - /// Adds combo colours to the list. - /// - [Obsolete("Use CustomComboColours directly.")] // can be removed 20220215 - void AddComboColours(params Color4[] colours); } } diff --git a/osu.Game/Beatmaps/Timing/TimeSignatures.cs b/osu.Game/Beatmaps/Timing/TimeSignatures.cs deleted file mode 100644 index 95c971eebf..0000000000 --- a/osu.Game/Beatmaps/Timing/TimeSignatures.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.ComponentModel; - -namespace osu.Game.Beatmaps.Timing -{ - [Obsolete("Use osu.Game.Beatmaps.Timing.TimeSignature instead.")] - public enum TimeSignatures // can be removed 20220722 - { - [Description("4/4")] - SimpleQuadruple = 4, - - [Description("3/4")] - SimpleTriple = 3 - } -} diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs deleted file mode 100644 index 0c1b4021a1..0000000000 --- a/osu.Game/Configuration/DatabasedSetting.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.ComponentModel.DataAnnotations.Schema; -using osu.Game.Database; - -namespace osu.Game.Configuration -{ - [Table("Settings")] - public class DatabasedSetting : IHasPrimaryKey // can be removed 20220315. - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public int? RulesetID { get; set; } - - public int? Variant { get; set; } - - public int? SkinInfoID { get; set; } - - [Column("Key")] - public string Key { get; set; } - - [Column("Value")] - public string StringValue - { - get => Value.ToString(); - set => Value = value; - } - - public object Value; - - public DatabasedSetting(string key, object value) - { - Key = key; - Value = value; - } - - /// - /// Constructor for derived classes that may require serialisation. - /// - public DatabasedSetting() - { - } - - public override string ToString() => $"{Key}=>{Value}"; - } -} diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index edcd020226..1a938c12e5 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -857,17 +857,7 @@ namespace osu.Game.Database if (legacyCollectionImporter.GetAvailableCount(storage).GetResultSafely() > 0) { - legacyCollectionImporter.ImportFromStorage(storage).ContinueWith(task => - { - if (task.Exception != null) - { - // can be removed 20221027 (just for initial safety). - Logger.Error(task.Exception.InnerException, "Collections could not be migrated to realm. Please provide your \"collection.db\" to the dev team."); - return; - } - - storage.Move("collection.db", "collection.db.migrated"); - }); + legacyCollectionImporter.ImportFromStorage(storage).ContinueWith(_ => storage.Move("collection.db", "collection.db.migrated")); } break; diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObjects.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObjects.cs deleted file mode 100644 index 7f926dd8b8..0000000000 --- a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObjects.cs +++ /dev/null @@ -1,18 +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.Collections.Generic; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Mods -{ - [Obsolete(@"Use the singular version IApplicableToDrawableHitObject instead.")] // Can be removed 20211216 - public interface IApplicableToDrawableHitObjects : IApplicableToDrawableHitObject - { - void ApplyToDrawableHitObjects(IEnumerable drawables); - - void IApplicableToDrawableHitObject.ApplyToDrawableHitObject(DrawableHitObject drawable) => ApplyToDrawableHitObjects(drawable.Yield()); - } -} diff --git a/osu.Game/Rulesets/Mods/ICreateReplay.cs b/osu.Game/Rulesets/Mods/ICreateReplay.cs deleted file mode 100644 index 1e5eeca92c..0000000000 --- a/osu.Game/Rulesets/Mods/ICreateReplay.cs +++ /dev/null @@ -1,22 +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.Collections.Generic; -using osu.Game.Beatmaps; -using osu.Game.Scoring; - -namespace osu.Game.Rulesets.Mods -{ - [Obsolete("Use ICreateReplayData instead")] // Can be removed 20220929 - public interface ICreateReplay : ICreateReplayData - { - public Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods); - - ModReplayData ICreateReplayData.CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) - { - var replayScore = CreateReplayScore(beatmap, mods); - return new ModReplayData(replayScore.Replay, new ModCreatedUser { Username = replayScore.ScoreInfo.User.Username }); - } - } -} diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index e4c91d3037..98df540de4 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -101,9 +101,6 @@ namespace osu.Game.Rulesets.Mods [JsonIgnore] public virtual bool ValidForMultiplayerAsFreeMod => true; - [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009 - public virtual bool Ranked => false; - /// /// Whether this mod requires configuration to apply changes to the game. /// diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index 6cafe0716d..83afda3a28 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -8,7 +8,6 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Replays; -using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { @@ -33,16 +32,6 @@ namespace osu.Game.Rulesets.Mods public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; - [Obsolete("Override CreateReplayData(IBeatmap, IReadOnlyList) instead")] // Can be removed 20220929 - public virtual Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { Replay = new Replay() }; - - public virtual ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) - { -#pragma warning disable CS0618 - var replayScore = CreateReplayScore(beatmap, mods); -#pragma warning restore CS0618 - - return new ModReplayData(replayScore.Replay, new ModCreatedUser { Username = replayScore.ScoreInfo.User.Username }); - } + public virtual ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) => new ModReplayData(new Replay(), new ModCreatedUser { Username = @"autoplay" }); } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index dec68a6c22..d624164013 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -196,18 +196,6 @@ namespace osu.Game.Rulesets.Objects.Drawables updateState(State.Value, true); } - /// - /// Applies a hit object to be represented by this . - /// - [Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")] // Can be removed 20211021. - public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry) - { - if (lifetimeEntry != null) - Apply(lifetimeEntry); - else - Apply(hitObject); - } - /// /// Applies a new to be represented by this . /// A new is automatically created and applied to this . diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs index e3ac054d1b..5bbd260d3f 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticItem.cs @@ -36,12 +36,6 @@ namespace osu.Game.Screens.Ranking.Statistics /// public readonly bool RequiresHitEvents; - [Obsolete("Use constructor which takes creation function instead.")] // Can be removed 20220803. - public StatisticItem([NotNull] string name, [NotNull] Drawable content, [CanBeNull] Dimension dimension = null) - : this(name, () => content, true, dimension) - { - } - /// /// Creates a new , to be displayed inside a in the results screen. /// diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 4e5d96ccb8..a9f660312e 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -66,8 +66,6 @@ namespace osu.Game.Skinning } } - void IHasComboColours.AddComboColours(params Color4[] colours) => CustomComboColours.AddRange(colours); - public Dictionary CustomColours { get; } = new Dictionary(); public readonly Dictionary ConfigDictionary = new Dictionary(); diff --git a/osu.Game/Skinning/SkinImporter.cs b/osu.Game/Skinning/SkinImporter.cs index 701dcdfc2d..f594a7b2d2 100644 --- a/osu.Game/Skinning/SkinImporter.cs +++ b/osu.Game/Skinning/SkinImporter.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading; using Newtonsoft.Json; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; @@ -33,9 +31,6 @@ namespace osu.Game.Skinning this.skinResources = skinResources; modelManager = new ModelManager(storage, realm); - - // can be removed 20220420. - populateMissingHashes(); } public override IEnumerable HandledExtensions => new[] { ".osk" }; @@ -158,18 +153,6 @@ namespace osu.Game.Skinning } modelManager.ReplaceFile(existingFile, stream, realm); - - // can be removed 20220502. - if (!ensureIniWasUpdated(item)) - { - Logger.Log($"Skin {item}'s skin.ini had issues and has been removed. Please report this and provide the problematic skin.", LoggingTarget.Database, LogLevel.Important); - - var existingIni = item.GetFile(@"skin.ini"); - if (existingIni != null) - item.Files.Remove(existingIni); - - writeNewSkinIni(); - } } } @@ -194,38 +177,6 @@ namespace osu.Game.Skinning } } - private bool ensureIniWasUpdated(SkinInfo item) - { - // This is a final consistency check to ensure that hash computation doesn't enter an infinite loop. - // With other changes to the surrounding code this should never be hit, but until we are 101% sure that there - // are no other cases let's avoid a hard startup crash by bailing and alerting. - - var instance = createInstance(item); - - return instance.Configuration.SkinInfo.Name == item.Name; - } - - private void populateMissingHashes() - { - Realm.Run(realm => - { - var skinsWithoutHashes = realm.All().Where(i => !i.Protected && string.IsNullOrEmpty(i.Hash)).ToArray(); - - foreach (SkinInfo skin in skinsWithoutHashes) - { - try - { - realm.Write(_ => skin.Hash = ComputeHash(skin)); - } - catch (Exception e) - { - modelManager.Delete(skin); - Logger.Error(e, $"Existing skin {skin} has been deleted during hash recomputation due to being invalid"); - } - } - }); - } - private Skin createInstance(SkinInfo item) => item.CreateInstance(skinResources); public void Save(Skin skin) From f9d2c30c3eb65fdf4074ac862f837ea94e639898 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 13:52:45 +0900 Subject: [PATCH 267/321] Add user toggle for application of safe areas --- .../TestSceneSafeAreaHandling.cs | 11 ++++++++++ osu.Game/Configuration/OsuConfigManager.cs | 4 +++- .../Graphics/Containers/ScalingContainer.cs | 11 ++++++++-- osu.Game/OsuGame.cs | 5 +++++ osu.Game/OsuGameBase.cs | 4 +++- .../Sections/Graphics/LayoutSettings.cs | 20 +++++++++++++++---- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSafeAreaHandling.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSafeAreaHandling.cs index 2ba0fa36c3..90365ec939 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSafeAreaHandling.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSafeAreaHandling.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; using osu.Game.Overlays.Settings; using osuTK.Graphics; @@ -24,6 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface private readonly Bindable safeAreaPaddingLeft = new BindableFloat { MinValue = 0, MaxValue = 200 }; private readonly Bindable safeAreaPaddingRight = new BindableFloat { MinValue = 0, MaxValue = 200 }; + private readonly Bindable applySafeAreaConsiderations = new Bindable(true); + protected override void LoadComplete() { base.LoadComplete(); @@ -84,6 +87,11 @@ namespace osu.Game.Tests.Visual.UserInterface Current = safeAreaPaddingRight, LabelText = "Right" }, + new SettingsCheckbox + { + LabelText = "Apply", + Current = applySafeAreaConsiderations, + }, } } } @@ -93,6 +101,7 @@ namespace osu.Game.Tests.Visual.UserInterface safeAreaPaddingBottom.BindValueChanged(_ => updateSafeArea()); safeAreaPaddingLeft.BindValueChanged(_ => updateSafeArea()); safeAreaPaddingRight.BindValueChanged(_ => updateSafeArea()); + applySafeAreaConsiderations.BindValueChanged(_ => updateSafeArea()); }); base.SetUpSteps(); @@ -107,6 +116,8 @@ namespace osu.Game.Tests.Visual.UserInterface Left = safeAreaPaddingLeft.Value, Right = safeAreaPaddingRight.Value, }; + + Game.LocalConfig.SetValue(OsuSetting.SafeAreaConsiderations, applySafeAreaConsiderations.Value); } [Test] diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 1378e1691a..e449837c92 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -154,6 +154,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.SongSelectRightMouseScroll, false); SetDefault(OsuSetting.Scaling, ScalingMode.Off); + SetDefault(OsuSetting.SafeAreaConsiderations, true); SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); @@ -370,6 +371,7 @@ namespace osu.Game.Configuration DiscordRichPresence, AutomaticallyDownloadWhenSpectating, ShowOnlineExplicitContent, - LastProcessedMetadataId + LastProcessedMetadataId, + SafeAreaConsiderations, } } diff --git a/osu.Game/Graphics/Containers/ScalingContainer.cs b/osu.Game/Graphics/Containers/ScalingContainer.cs index 17c51129a7..11e57d4be3 100644 --- a/osu.Game/Graphics/Containers/ScalingContainer.cs +++ b/osu.Game/Graphics/Containers/ScalingContainer.cs @@ -29,6 +29,7 @@ namespace osu.Game.Graphics.Containers private Bindable sizeY; private Bindable posX; private Bindable posY; + private Bindable applySafeAreaPadding; private Bindable safeAreaPadding; @@ -132,6 +133,9 @@ namespace osu.Game.Graphics.Containers posY = config.GetBindable(OsuSetting.ScalingPositionY); posY.ValueChanged += _ => Scheduler.AddOnce(updateSize); + applySafeAreaPadding = config.GetBindable(OsuSetting.SafeAreaConsiderations); + applySafeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize)); + safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy(); safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize)); } @@ -192,7 +196,7 @@ namespace osu.Game.Graphics.Containers bool requiresMasking = targetRect.Size != Vector2.One // For the top level scaling container, for now we apply masking if safe areas are in use. // In the future this can likely be removed as more of the actual UI supports overflowing into the safe areas. - || (targetMode == ScalingMode.Everything && safeAreaPadding.Value.Total != Vector2.Zero); + || (targetMode == ScalingMode.Everything && (applySafeAreaPadding.Value && safeAreaPadding.Value.Total != Vector2.Zero)); if (requiresMasking) sizableContainer.Masking = true; @@ -225,6 +229,9 @@ namespace osu.Game.Graphics.Containers [Resolved] private ISafeArea safeArea { get; set; } + [Resolved] + private OsuConfigManager config { get; set; } + private readonly bool confineHostCursor; private readonly LayoutValue cursorRectCache = new LayoutValue(Invalidation.RequiredParentSizeToFit); @@ -259,7 +266,7 @@ namespace osu.Game.Graphics.Containers { if (host.Window == null) return; - bool coversWholeScreen = Size == Vector2.One && safeArea.SafeAreaPadding.Value.Total == Vector2.Zero; + bool coversWholeScreen = Size == Vector2.One && (!config.Get(OsuSetting.SafeAreaConsiderations) || safeArea.SafeAreaPadding.Value.Total == Vector2.Zero); host.Window.CursorConfineRect = coversWholeScreen ? null : ToScreenSpace(DrawRectangle).AABBFloat; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2bdcb57f2a..b3009fb85f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -179,6 +179,8 @@ namespace osu.Game private Bindable configRuleset; + private Bindable applySafeAreaConsiderations; + private Bindable uiScale; private Bindable configSkin; @@ -312,6 +314,9 @@ namespace osu.Game SelectedMods.BindValueChanged(modsChanged); Beatmap.BindValueChanged(beatmapChanged, true); + + applySafeAreaConsiderations = LocalConfig.GetBindable(OsuSetting.SafeAreaConsiderations); + applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All); } private ExternalLinkOpener externalLinkOpener; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7d9ed7bf3e..4c31158597 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -189,6 +189,8 @@ namespace osu.Game private RealmAccess realm; + protected SafeAreaContainer SafeAreaContainer { get; private set; } + /// /// For now, this is used as a source specifically for beat synced components. /// Going forward, it could potentially be used as the single source-of-truth for beatmap timing. @@ -341,7 +343,7 @@ namespace osu.Game GlobalActionContainer globalBindings; - base.Content.Add(new SafeAreaContainer + base.Content.Add(SafeAreaContainer = new SafeAreaContainer { SafeAreaOverrideEdges = SafeAreaOverrideEdges, RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 59b56522a4..f77e1bfc6a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -20,6 +20,7 @@ using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Settings.Sections.Graphics @@ -50,6 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private SettingsDropdown resolutionDropdown = null!; private SettingsDropdown displayDropdown = null!; private SettingsDropdown windowModeDropdown = null!; + private SettingsCheckbox safeAreaConsiderationsCheckbox = null!; private Bindable scalingPositionX = null!; private Bindable scalingPositionY = null!; @@ -101,6 +103,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics ItemSource = resolutions, Current = sizeFullscreen }, + safeAreaConsiderationsCheckbox = new SettingsCheckbox + { + LabelText = "Shrink game to avoid cameras and notches", + Current = osuConfig.GetBindable(OsuSetting.SafeAreaConsiderations), + }, new SettingsSlider { LabelText = GraphicsSettingsStrings.UIScaling, @@ -166,7 +173,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown.Current.BindValueChanged(_ => { - updateDisplayModeDropdowns(); + updateDisplaySettingsVisibility(); updateScreenModeWarning(); }, true); @@ -191,7 +198,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics .Distinct()); } - updateDisplayModeDropdowns(); + updateDisplaySettingsVisibility(); }), true); scalingMode.BindValueChanged(_ => @@ -221,11 +228,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics Scheduler.AddOnce(d => { displayDropdown.Items = d; - updateDisplayModeDropdowns(); + updateDisplaySettingsVisibility(); }, displays); } - private void updateDisplayModeDropdowns() + private void updateDisplaySettingsVisibility() { if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen) resolutionDropdown.Show(); @@ -236,6 +243,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics displayDropdown.Show(); else displayDropdown.Hide(); + + if (host.Window.SafeAreaPadding.Value.Total != Vector2.Zero) + safeAreaConsiderationsCheckbox.Show(); + else + safeAreaConsiderationsCheckbox.Hide(); } private void updateScreenModeWarning() From 10c658e4408d565fca826f8ad7afe29bc6435f3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 14:34:24 +0900 Subject: [PATCH 268/321] Ensure `updateVisualDefinition` is called once on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index b5b04823a7..6fd1546d30 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (editorBeatmap != null) selectedObjects.BindTo(editorBeatmap.SelectedHitObjects); - selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition()); + selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition(), true); } public override bool HandleQuickDeletion() From 300ffbb500e0b51ef3eebd363ea25904da109f74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 14:35:55 +0900 Subject: [PATCH 269/321] Don't bother checking for null `editorBeatmap` --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 6fd1546d30..36ee7c2460 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } - private bool hasSingleObjectSelected => editorBeatmap == null || selectedObjects.Count == 1; + private bool hasSingleObjectSelected => selectedObjects.Count == 1; protected override void Update() { From b3219526a57e415e870082ff176a03e65610cf27 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 28 Oct 2022 14:42:42 +0900 Subject: [PATCH 270/321] Populate beatmap ruleset in SoloScoreInfo.ToScoreInfo() --- .../API/Requests/Responses/SoloScoreInfo.cs | 62 ++++++++++++------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index 57586e3d77..77dcfd39e3 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -154,10 +154,8 @@ namespace osu.Game.Online.API.Requests.Responses var mods = Mods.Select(apiMod => apiMod.ToMod(rulesetInstance)).ToArray(); - var scoreInfo = ToScoreInfo(mods); - + var scoreInfo = ToScoreInfo(mods, beatmap); scoreInfo.Ruleset = ruleset; - if (beatmap != null) scoreInfo.BeatmapInfo = beatmap; return scoreInfo; } @@ -166,25 +164,47 @@ namespace osu.Game.Online.API.Requests.Responses /// Create a from an API score instance. /// /// The mod instances, resolved from a ruleset. - /// - public ScoreInfo ToScoreInfo(Mod[] mods) => new ScoreInfo + /// The object to populate the scores' beatmap with. + /// + /// If this is a type, then the score will be fully populated with the given object. + /// Otherwise, if this is an type (e.g. ), then only the beatmap ruleset will be populated. + /// Otherwise, if this is null, then the beatmap ruleset will not be populated. + /// The online beatmap ID is populated in all cases. + /// + /// + /// The populated . + public ScoreInfo ToScoreInfo(Mod[] mods, IBeatmapInfo? beatmap = null) { - OnlineID = OnlineID, - User = User ?? new APIUser { Id = UserID }, - BeatmapInfo = new BeatmapInfo { OnlineID = BeatmapID }, - Ruleset = new RulesetInfo { OnlineID = RulesetID }, - Passed = Passed, - TotalScore = TotalScore, - Accuracy = Accuracy, - MaxCombo = MaxCombo, - Rank = Rank, - Statistics = Statistics, - MaximumStatistics = MaximumStatistics, - Date = EndedAt, - Hash = HasReplay ? "online" : string.Empty, // TODO: temporary? - Mods = mods, - PP = PP, - }; + var score = new ScoreInfo + { + OnlineID = OnlineID, + User = User ?? new APIUser { Id = UserID }, + BeatmapInfo = new BeatmapInfo { OnlineID = BeatmapID }, + Ruleset = new RulesetInfo { OnlineID = RulesetID }, + Passed = Passed, + TotalScore = TotalScore, + Accuracy = Accuracy, + MaxCombo = MaxCombo, + Rank = Rank, + Statistics = Statistics, + MaximumStatistics = MaximumStatistics, + Date = EndedAt, + Hash = HasReplay ? "online" : string.Empty, // TODO: temporary? + Mods = mods, + PP = PP, + }; + + if (beatmap is BeatmapInfo realmBeatmap) + score.BeatmapInfo = realmBeatmap; + else if (beatmap != null) + { + score.BeatmapInfo.Ruleset.OnlineID = beatmap.Ruleset.OnlineID; + score.BeatmapInfo.Ruleset.Name = beatmap.Ruleset.Name; + score.BeatmapInfo.Ruleset.ShortName = beatmap.Ruleset.ShortName; + } + + return score; + } /// /// Creates a from a local score for score submission. From c1e9398c19d3e74850c9f7fe546d8fefdabefa2f Mon Sep 17 00:00:00 2001 From: nullium21 Date: Fri, 28 Oct 2022 09:19:34 +0300 Subject: [PATCH 271/321] Fix regex by allowing file extensions + typed new Apparently, the _whole_ filename was checked with GetValidArchiveContentFilename, not just the stem. --- osu.Game/Extensions/ModelExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 8acc079805..73a87c7003 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -137,7 +137,7 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - private static Regex invalid_filename_chars = new(@"(?!$)[^A-Za-z0-9_()[\] -]", RegexOptions.Compiled); + private static Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. -]", RegexOptions.Compiled); /// /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. From 6901ed997d353628005b524ad553d7eed9c83cde Mon Sep 17 00:00:00 2001 From: nullium21 Date: Fri, 28 Oct 2022 09:21:47 +0300 Subject: [PATCH 272/321] One more little change Damn, i forgor. Sorry, CI runners. --- osu.Game/Extensions/ModelExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 73a87c7003..f8db5f4e88 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -137,7 +137,7 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - private static Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. -]", RegexOptions.Compiled); + private static readonly Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. \-]", RegexOptions.Compiled); /// /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. From 87328dd1f535ec4f5c799b73ac8207ed439b1596 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 27 Oct 2022 23:36:30 -0700 Subject: [PATCH 273/321] Fix bar hit error meter labels not clearing when setting to none --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index d6b9c62369..e7b2ce1672 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -279,6 +279,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters switch (style) { case LabelStyles.None: + labelEarly.Clear(); + labelLate.Clear(); break; case LabelStyles.Icons: From 8c057b3af7a3d97727007b792017feb1a65660d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 17:26:53 +0900 Subject: [PATCH 274/321] Add comment explaining why handlers are added in `OsuGameBase` --- osu.Game/OsuGameBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1177f01609..c94ad34f11 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -526,6 +526,11 @@ namespace osu.Game /// Should be overriden per-platform to provide settings for platform-specific handlers. public virtual SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler) { + // One would think that this could be moved to the `OsuGameDesktop` class, but doing so means that + // OsuGameTestScenes will not show any input options (as they are based on OsuGame not OsuGameDesktop). + // + // This in turn makes it hard for ruleset creators to adjust input settings while testing their ruleset + // within the test browser interface. if (RuntimeInfo.IsDesktop) { switch (handler) From 66ed77ac91f7a9ab4cd6297833fba3f52a044bf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 18:04:28 +0900 Subject: [PATCH 275/321] Rename and add documentation to function --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/LegacyExporter.cs | 2 +- osu.Game/Extensions/ModelExtensions.cs | 16 +++++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index befc56d244..965cc43815 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -340,7 +340,7 @@ namespace osu.Game.Beatmaps static string createBeatmapFilenameFromMetadata(BeatmapInfo beatmapInfo) { var metadata = beatmapInfo.Metadata; - return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename(); + return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidFilename(); } } diff --git a/osu.Game/Database/LegacyExporter.cs b/osu.Game/Database/LegacyExporter.cs index d9fdc40abc..16d7441dde 100644 --- a/osu.Game/Database/LegacyExporter.cs +++ b/osu.Game/Database/LegacyExporter.cs @@ -37,7 +37,7 @@ namespace osu.Game.Database /// The item to export. public void Export(TModel item) { - string filename = $"{item.GetDisplayString().GetValidArchiveContentFilename()}{FileExtension}"; + string filename = $"{item.GetDisplayString().GetValidFilename()}{FileExtension}"; using (var stream = exportStorage.CreateFileSafely(filename)) ExportModelTo(item, stream); diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index f8db5f4e88..efb3c4d633 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -15,6 +15,8 @@ namespace osu.Game.Extensions { public static class ModelExtensions { + private static readonly Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. \-]", RegexOptions.Compiled); + /// /// Get the relative path in osu! storage for this file. /// @@ -137,14 +139,14 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - private static readonly Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. \-]", RegexOptions.Compiled); - /// - /// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories. + /// Create a valid filename which should work across all platforms. /// - public static string GetValidArchiveContentFilename(this string filename) - { - return invalid_filename_chars.Replace(filename, "_"); - } + /// + /// This function replaces all characters not included in a very pessimistic list which should be compatible + /// across all operating systems. We are using this in place of as + /// that function does not have per-platform considerations (and is only made to work on windows). + /// + public static string GetValidFilename(this string filename) => invalid_filename_chars.Replace(filename, "_"); } } From f46a9d683aebe2f3bef438792bee1921466e727f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 18:07:25 +0900 Subject: [PATCH 276/321] Fix null reference in headless tests --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index f77e1bfc6a..7f0bded806 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -244,7 +244,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics else displayDropdown.Hide(); - if (host.Window.SafeAreaPadding.Value.Total != Vector2.Zero) + if (host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero) safeAreaConsiderationsCheckbox.Show(); else safeAreaConsiderationsCheckbox.Hide(); From e87b541c5893d2c642abac4f288e83cd53e2d8f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 18:29:55 +0900 Subject: [PATCH 277/321] Add fade to banana's lighting as it approaches the catcher This also avoids using the `UprightScalingContainer` (and allows for scale adjust in the process). --- .../Skinning/Argon/ArgonBananaPiece.cs | 21 ++++++++++++++++--- .../Skinning/Default/BananaPiece.cs | 6 ++---- .../Skinning/Default/CatchHitObjectPiece.cs | 2 +- .../Skinning/Default/FruitPiece.cs | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs index 49edc80770..1a37136f53 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs @@ -5,9 +5,10 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects; using osuTK; using osuTK.Graphics; @@ -15,10 +16,14 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { internal class ArgonBananaPiece : ArgonFruitPiece { + private Container stabilisedPieceContainer = null!; + + protected override Drawable BorderPiece => stabilisedPieceContainer; + [BackgroundDependencyLoader] private void load() { - AddInternal(new UprightAspectMaintainingContainer + AddInternal(stabilisedPieceContainer = new Container { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -44,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon Width = 1.6f, Height = 2, }, - new Box + new Circle { Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)), RelativeSizeAxes = Axes.X, @@ -78,5 +83,15 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon } }); } + + protected override void Update() + { + base.Update(); + + float scale = 0.5f + 0.5f * (1 / (ObjectState.DisplaySize.X / (CatchHitObject.OBJECT_RADIUS * 2))); + + stabilisedPieceContainer.Rotation = -ObjectState.DisplayRotation; + stabilisedPieceContainer.Scale = new Vector2(scale); + } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs index 27252594af..359756f159 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/BananaPiece.cs @@ -1,21 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; namespace osu.Game.Rulesets.Catch.Skinning.Default { public class BananaPiece : CatchHitObjectPiece { - protected override BorderPiece BorderPiece { get; } + protected override Drawable BorderPiece { get; } public BananaPiece() { RelativeSizeAxes = Axes.Both; - InternalChildren = new Drawable[] + InternalChildren = new[] { new BananaPulpFormation { diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index fd1047f27e..3b8df6ee6f 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default /// A part of this piece that will be faded out while falling in the playfield. /// [CanBeNull] - protected virtual BorderPiece BorderPiece => null; + protected virtual Drawable BorderPiece => null; /// /// A part of this piece that will be only visible when is true. diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 9dce0e9129..adee960c3c 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default public readonly Bindable VisualRepresentation = new Bindable(); - protected override BorderPiece BorderPiece { get; } + protected override Drawable BorderPiece { get; } protected override Drawable HyperBorderPiece { get; } public FruitPiece() From 40efa1603bf1d8c0ea8688753a9f1cfab110df15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 19:35:50 +0900 Subject: [PATCH 278/321] Adjust metrics to make banana lens flares look better --- .../Objects/Drawables/CaughtObject.cs | 2 + .../DrawablePalpableCatchHitObject.cs | 2 + .../Objects/Drawables/IHasCatchObjectState.cs | 2 + .../Skinning/Argon/ArgonBananaPiece.cs | 87 ++++++++++++------- 4 files changed, 62 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index fd0ffbd032..ddfbb34435 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRotation => Rotation; + public double DisplayStartTime => HitObject.StartTime; + /// /// 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/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 5de372852b..dd09b6c06d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; + public double DisplayStartTime => LifetimeStart; + Bindable IHasCatchObjectState.AccentColour => AccentColour; public Bindable HyperDash { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 93c80b09db..f30ef0831a 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { PalpableCatchHitObject HitObject { get; } + double DisplayStartTime { get; } + Bindable AccentColour { get; } Bindable HyperDash { get; } diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs index 1a37136f53..f8a71cbec9 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Objects; using osuTK; using osuTK.Graphics; @@ -18,46 +19,53 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { private Container stabilisedPieceContainer = null!; - protected override Drawable BorderPiece => stabilisedPieceContainer; + private Drawable fadeContent = null!; [BackgroundDependencyLoader] private void load() { - AddInternal(stabilisedPieceContainer = new Container + AddInternal(fadeContent = new Container { RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Children = new Drawable[] { - new Circle + stabilisedPieceContainer = new Container { - Colour = Color4.White.Opacity(0.4f), + RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Blending = BlendingParameters.Additive, - Size = new Vector2(8), - Scale = new Vector2(30, 1), - }, - new Box - { - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White), - RelativeSizeAxes = Axes.X, - Blending = BlendingParameters.Additive, - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - Width = 1.6f, - Height = 2, - }, - new Circle - { - Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)), - RelativeSizeAxes = Axes.X, - Blending = BlendingParameters.Additive, - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - Width = 1.6f, - Height = 2, + Children = new Drawable[] + { + new Circle + { + Colour = Color4.White.Opacity(0.4f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Size = new Vector2(8), + Scale = new Vector2(25, 1), + }, + new Box + { + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.8f)), + RelativeSizeAxes = Axes.X, + Blending = BlendingParameters.Additive, + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + Width = 1.6f, + Height = 2, + }, + new Circle + { + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White.Opacity(0)), + RelativeSizeAxes = Axes.X, + Blending = BlendingParameters.Additive, + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + Width = 1.6f, + Height = 2, + }, + } }, new Circle { @@ -88,10 +96,27 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon { base.Update(); - float scale = 0.5f + 0.5f * (1 / (ObjectState.DisplaySize.X / (CatchHitObject.OBJECT_RADIUS * 2))); + const float parent_scale_application = 0.4f; + + // relative to time on screen + const float lens_flare_start = 0.3f; + const float lens_flare_end = 0.3f; + + // Undo some of the parent scale being applied to make the lens flare feel a bit better.. + float scale = parent_scale_application + (1 - parent_scale_application) * (1 / (ObjectState.DisplaySize.X / (CatchHitObject.OBJECT_RADIUS * 2))); stabilisedPieceContainer.Rotation = -ObjectState.DisplayRotation; - stabilisedPieceContainer.Scale = new Vector2(scale); + stabilisedPieceContainer.Scale = new Vector2(scale, 1); + + double duration = ObjectState.HitObject.StartTime - ObjectState.DisplayStartTime; + + fadeContent.Alpha = MathHelper.Clamp( + Interpolation.ValueAt( + Time.Current, 1f, 0f, + ObjectState.DisplayStartTime + duration * lens_flare_start, + ObjectState.DisplayStartTime + duration * lens_flare_end, + Easing.OutQuint + ), 0, 1); } } } From 0c25658805458f016f2a2ca64729134a2b00b400 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 Oct 2022 21:58:38 +0900 Subject: [PATCH 279/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 5d6c32cea8..b691751f13 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5d1a834b0e..9dd0d18817 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index bf218f856a..6dce938ebf 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From 21d68bfb84dfeaf6605623b2700cec2faa5ed147 Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Fri, 28 Oct 2022 16:40:21 +0200 Subject: [PATCH 280/321] Delete unused button --- .../Screens/Edit/Components/CircularButton.cs | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Components/CircularButton.cs diff --git a/osu.Game/Screens/Edit/Components/CircularButton.cs b/osu.Game/Screens/Edit/Components/CircularButton.cs deleted file mode 100644 index 74e4162102..0000000000 --- a/osu.Game/Screens/Edit/Components/CircularButton.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using osu.Game.Graphics.UserInterface; -using osuTK; - -namespace osu.Game.Screens.Edit.Components -{ - public class CircularButton : OsuButton - { - private const float width = 125; - private const float height = 30; - - public CircularButton() - { - Size = new Vector2(width, height); - } - - protected override void Update() - { - base.Update(); - Content.CornerRadius = DrawHeight / 2f; - Content.CornerExponent = 2; - } - } -} From 19e0b9085c473a2470e86b167d1e2ae05c28ac91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Oct 2022 00:24:33 +0900 Subject: [PATCH 281/321] Remove redundant parameters --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs | 2 +- osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs | 2 +- .../NonVisual/CustomTourneyDirectoryTest.cs | 2 +- osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index de6ca7dd38..a41ed17d78 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { // For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either. // ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter - base.ApplyTransformsAt(time, false); + base.ApplyTransformsAt(time); } private bool tracking; diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs index 604b87dc4c..9079ecdc48 100644 --- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs +++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Collections.IO } // Name matches the automatically chosen name from `CleanRunHeadlessGameHost` above, so we end up using the same storage location. - using (HeadlessGameHost host = new TestRunHeadlessGameHost(firstRunName, null)) + using (HeadlessGameHost host = new TestRunHeadlessGameHost(firstRunName)) { try { diff --git a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs index f778e67f58..45dffdc94a 100644 --- a/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/CustomTourneyDirectoryTest.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tournament.Tests.NonVisual [Test] public void TestCustomDirectory() { - using (HeadlessGameHost host = new TestRunHeadlessGameHost(nameof(TestCustomDirectory), null)) // don't use clean run as we are writing a config file. + using (HeadlessGameHost host = new TestRunHeadlessGameHost(nameof(TestCustomDirectory))) // don't use clean run as we are writing a config file. { string osuDesktopStorage = Path.Combine(host.UserStoragePaths.First(), nameof(TestCustomDirectory)); const string custom_tournament = "custom"; diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index 1bbbcc3661..ca6354cb48 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests.NonVisual public void CheckIPCLocation() { // don't use clean run because files are being written before osu! launches. - using (var host = new TestRunHeadlessGameHost(nameof(CheckIPCLocation), null)) + using (var host = new TestRunHeadlessGameHost(nameof(CheckIPCLocation))) { string basePath = Path.Combine(host.UserStoragePaths.First(), nameof(CheckIPCLocation)); From a985151e3173f5605d49288bccf4b5346fb32d3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Oct 2022 18:06:25 +0900 Subject: [PATCH 282/321] Fix incorrect constant --- osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs index f8a71cbec9..9a657c9216 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon // relative to time on screen const float lens_flare_start = 0.3f; - const float lens_flare_end = 0.3f; + const float lens_flare_end = 0.8f; // Undo some of the parent scale being applied to make the lens flare feel a bit better.. float scale = parent_scale_application + (1 - parent_scale_application) * (1 / (ObjectState.DisplaySize.X / (CatchHitObject.OBJECT_RADIUS * 2))); From 8c9250285f0fe2188a56451b67c7643b81b49dbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 Oct 2022 18:09:27 +0900 Subject: [PATCH 283/321] Revert incorrect removal default value (causing a new warning) --- .../Objects/Drawables/DrawableSliderBall.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index a41ed17d78..9966ad3a90 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -79,8 +79,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override void ApplyTransformsAt(double time, bool propagateChildren = false) { // For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either. - // ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter - base.ApplyTransformsAt(time); + + // ReSharper disable once RedundantArgumentDefaultValue + base.ApplyTransformsAt(time, false); } private bool tracking; From d56786b5ff051ab358b5e0b4c180fdc7743b247d Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 29 Oct 2022 15:25:16 -0700 Subject: [PATCH 284/321] Fix toolbar visual bug when toggling clock displays --- osu.Game/Overlays/Toolbar/Toolbar.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 82fa20aa9c..9d0f43c45a 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -141,6 +141,8 @@ namespace osu.Game.Overlays.Toolbar Name = "Right buttons", RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, Children = new Drawable[] { new Box From a753369d3e3096ef75087abdf34ff965abcb7a59 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 30 Oct 2022 17:21:50 +0800 Subject: [PATCH 285/321] Reuse function `TimeAtPosition()` and `PositionAtTime()`. --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index a73ada76f5..3a93499527 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void seekTrackToCurrent() { - double target = Current / Content.DrawWidth * editorClock.TrackLength; + double target = TimeAtPosition(Current); editorClock.Seek(Math.Min(editorClock.TrackLength, target)); } @@ -264,7 +264,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (handlingDragInput) editorClock.Stop(); - ScrollTo((float)(editorClock.CurrentTime / editorClock.TrackLength) * Content.DrawWidth, false); + float position = PositionAtTime(editorClock.CurrentTime); + ScrollTo(position, false); } protected override bool OnMouseDown(MouseDownEvent e) From f5e11343c28eebb15a0d6d0916b214a0df7dabbb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Oct 2022 21:24:58 +0900 Subject: [PATCH 286/321] Apply nullability to HUD test scene --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index a984f508ea..325839a6c9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -1,12 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -26,9 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneHUDOverlay : OsuManualInputManagerTestScene { - private OsuConfigManager localConfig; + private OsuConfigManager localConfig = null!; - private HUDOverlay hudOverlay; + private HUDOverlay hudOverlay = null!; [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset()); @@ -220,7 +219,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded); } - private void createNew(Action action = null) + private void createNew(Action? action = null) { AddStep("create overlay", () => { @@ -239,7 +238,9 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void Dispose(bool isDisposing) { - localConfig?.Dispose(); + if (localConfig.IsNotNull()) + localConfig.Dispose(); + base.Dispose(isDisposing); } } From 8399261c77a53ffb66672d61af7de85440a1a344 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Oct 2022 21:38:31 +0900 Subject: [PATCH 287/321] Add failing test showing hold for menu not working when HUD is hidden --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 325839a6c9..75510fa822 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Configuration; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; @@ -148,6 +149,41 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent); } + [Test] + public void TestHoldForMenuDoesWorkWhenHidden() + { + bool activated = false; + + HoldForMenuButton getHoldForMenu() => hudOverlay.ChildrenOfType().Single(); + + createNew(); + + AddStep("bind action", () => + { + activated = false; + + var holdForMenu = getHoldForMenu(); + + holdForMenu.Action += () => activated = true; + }); + + AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false); + AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent); + + AddStep("attempt activate", () => + { + InputManager.MoveMouseTo(getHoldForMenu().OfType().Single()); + InputManager.PressButton(MouseButton.Left); + }); + + AddUntilStep("activated", () => activated); + + AddStep("release mouse button", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + } + [Test] public void TestInputDoesntWorkWhenHUDHidden() { From 1035b5fb1905b0a07f910f888e8c253582b13e5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 Oct 2022 21:38:45 +0900 Subject: [PATCH 288/321] Fix hold to pause button not working when HUD is hidden --- osu.Game/Screens/Play/HUDOverlay.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 7833c2d7fa..2791f5ff8f 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -39,9 +39,16 @@ namespace osu.Game.Screens.Play /// public float BottomScoringElementsHeight { get; private set; } - // HUD uses AlwaysVisible on child components so they can be in an updated state for next display. - // Without blocking input, this would also allow them to be interacted with in such a state. - public override bool PropagatePositionalInputSubTree => ShowHud.Value; + protected override bool ShouldBeConsideredForInput(Drawable child) + { + // HUD uses AlwaysVisible on child components so they can be in an updated state for next display. + // Without blocking input, this would also allow them to be interacted with in such a state. + if (ShowHud.Value) + return base.ShouldBeConsideredForInput(child); + + // hold to quit button should always be interactive. + return child == bottomRightElements; + } public readonly KeyCounterDisplay KeyCounter; public readonly ModDisplay ModDisplay; From 3f17c206d2cea58bbc1f143d74fee22bdddd1f2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 Oct 2022 12:49:50 +0900 Subject: [PATCH 289/321] Update `fastlane` and dependencies --- Gemfile.lock | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cae682ec2b..07ca3542f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,25 +3,25 @@ GEM specs: CFPropertyList (3.0.5) rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.601.0) - aws-sdk-core (3.131.2) + aws-partitions (1.653.0) + aws-sdk-core (3.166.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.57.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-kms (1.59.0) + aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-s3 (1.117.1) + aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.0) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) @@ -34,10 +34,10 @@ GEM rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) + dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.92.3) - faraday (1.10.0) + excon (0.93.1) + faraday (1.10.2) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -66,7 +66,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.206.2) + fastlane (2.210.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -110,9 +110,9 @@ GEM souyuz (= 0.11.1) fastlane-plugin-xamarin (0.6.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.23.0) - google-apis-core (>= 0.6, < 2.a) - google-apis-core (0.6.0) + google-apis-androidpublisher_v3 (0.29.0) + google-apis-core (>= 0.9.0, < 2.a) + google-apis-core (0.9.1) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -121,27 +121,27 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.12.0) - google-apis-core (>= 0.6, < 2.a) - google-apis-playcustomapp_v1 (0.9.0) - google-apis-core (>= 0.6, < 2.a) - google-apis-storage_v1 (0.16.0) - google-apis-core (>= 0.6, < 2.a) + google-apis-iamcredentials_v1 (0.15.0) + google-apis-core (>= 0.9.0, < 2.a) + google-apis-playcustomapp_v1 (0.12.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.36.2) + google-cloud-errors (1.3.0) + google-cloud-storage (1.43.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.2.0) + googleauth (1.3.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -154,22 +154,22 @@ GEM httpclient (2.8.3) jmespath (1.6.1) json (2.6.2) - jwt (2.4.1) + jwt (2.5.0) memoist (0.16.2) mini_magick (4.11.0) mini_mime (1.1.2) - mini_portile2 (2.7.1) + mini_portile2 (2.8.0) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) - nokogiri (1.13.1) - mini_portile2 (~> 2.7.0) + nokogiri (1.13.9) + mini_portile2 (~> 2.8.0) racc (~> 1.4) optparse (0.1.1) os (1.1.4) plist (3.6.0) - public_suffix (4.0.7) + public_suffix (5.0.0) racc (1.6.0) rake (13.0.6) representable (3.2.0) From 4213abfc46b499db14dd65dedf514f906f73f5c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 Oct 2022 12:55:23 +0900 Subject: [PATCH 290/321] Adjust testflight cleanup interval to 30 days Been using this for a while but haven't committed it. --- fastlane/Fastfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index cc5abf5b03..716115e5c6 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -138,10 +138,10 @@ platform :ios do end lane :testflight_prune_dry do - clean_testflight_testers(days_of_inactivity:45, dry_run: true) + clean_testflight_testers(days_of_inactivity:30, dry_run: true) end lane :testflight_prune do - clean_testflight_testers(days_of_inactivity: 45) + clean_testflight_testers(days_of_inactivity: 30) end end From 706adfb28c290eda95bda8e982e062ad79760c18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 13:57:34 +0900 Subject: [PATCH 291/321] Improve messaging when ruleset load fails --- osu.Game/Rulesets/RulesetStore.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index fdbcd0ed1e..e2b8cd2c4e 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -158,7 +158,7 @@ namespace osu.Game.Rulesets } catch (Exception e) { - LogFailedLoad(assembly.FullName, e); + LogFailedLoad(assembly.GetName().Name.Split('.').Last(), e); } } @@ -168,14 +168,14 @@ namespace osu.Game.Rulesets GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + protected void Dispose(bool disposing) { AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly; } protected void LogFailedLoad(string name, Exception exception) { - Logger.Log($"Could not load ruleset {name}. Please check for an update from the developer.", level: LogLevel.Error); + Logger.Log($"Could not load ruleset \"{name}\". Please check for an update from the developer.", level: LogLevel.Error); Logger.Log($"Ruleset load failed: {exception}"); } From 2a88409dfe65fd5e48c52bbfdb0397e8d8b4f575 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 15:00:23 +0900 Subject: [PATCH 292/321] Fix time snap of sliders not matching when SV is not 1.0x This regressed with https://github.com/ppy/osu/pull/20850 because the function was used in other places which expect it to factor slider velocity into the equation. Rather than reverting, I've added a new argument, as based on the method naming alone it was hard to discern whether SV should actually be considered. The reason for the change in #20850 was to avoid the SV coming in from a reference object which may not have a correct SV in the first place. In such cases, passing `false` to the function will give the expected behaviour. --- osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 6 +++--- osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs | 3 ++- .../Screens/Edit/Compose/Components/DistanceSnapGrid.cs | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 53b6db2277..01a49c7dea 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual.Editing IBindable IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier; - public float GetBeatSnapDistanceAt(HitObject referenceObject) => beat_snap_distance; + public float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) => beat_snap_distance; public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration; diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index ca7ca79813..840ed7682a 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -259,9 +259,9 @@ namespace osu.Game.Rulesets.Edit return true; } - public virtual float GetBeatSnapDistanceAt(HitObject referenceObject) + public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) { - return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor); + return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor); } public virtual float DurationToDistance(HitObject referenceObject, double duration) @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Edit public virtual double DistanceToDuration(HitObject referenceObject, float distance) { double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); - return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength; + return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength * referenceObject.DifficultyControlPoint.SliderVelocity; } public virtual double FindSnappedDuration(HitObject referenceObject, float distance) diff --git a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs index 5ad1cc78ff..6fbd994e23 100644 --- a/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IDistanceSnapProvider.cs @@ -27,8 +27,9 @@ namespace osu.Game.Rulesets.Edit /// Retrieves the distance between two points within a timing point that are one beat length apart. /// /// An object to be used as a reference point for this operation. + /// Whether the 's slider velocity should be factored into the returned distance. /// The distance between two points residing in the timing point that are one beat length apart. - float GetBeatSnapDistanceAt(HitObject referenceObject); + float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true); /// /// Converts a duration to a distance without applying any snapping. diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 69c7fc2775..c179e7f0c2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value; - float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject); + float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject, false); DistanceBetweenTicks = beatSnapDistance * distanceSpacingMultiplier; From e8b791429569d3f9513265ae7ae4f23c2754aba7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 15:13:05 +0900 Subject: [PATCH 293/321] Add test coverage of new parameter --- ...tSceneHitObjectComposerDistanceSnapping.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index a98f931e7a..6fb21e38b0 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Editing { AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); - assertSnapDistance(100 * multiplier); + assertSnapDistance(100 * multiplier, null, true); } [TestCase(1)] @@ -79,7 +79,20 @@ namespace osu.Game.Tests.Editing { SliderVelocity = multiplier } - }); + }, false); + } + + [TestCase(1)] + [TestCase(2)] + public void TestSpeedMultiplierDoesChangeDistanceSnap(float multiplier) + { + assertSnapDistance(100 * multiplier, new HitObject + { + DifficultyControlPoint = new DifficultyControlPoint + { + SliderVelocity = multiplier + } + }, true); } [TestCase(1)] @@ -88,7 +101,7 @@ namespace osu.Game.Tests.Editing { AddStep($"set divisor = {divisor}", () => BeatDivisor.Value = divisor); - assertSnapDistance(100f / divisor); + assertSnapDistance(100f / divisor, null, true); } [Test] @@ -197,8 +210,8 @@ namespace osu.Game.Tests.Editing assertSnappedDistance(400, 400); } - private void assertSnapDistance(float expectedDistance, HitObject? hitObject = null) - => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(hitObject ?? new HitObject()), () => Is.EqualTo(expectedDistance)); + private void assertSnapDistance(float expectedDistance, HitObject? refereneObject, bool includeSliderVelocity) + => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(refereneObject ?? new HitObject(), includeSliderVelocity), () => Is.EqualTo(expectedDistance)); private void assertDurationToDistance(double duration, float expectedDistance) => AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(new HitObject(), duration) == expectedDistance); From d807d9d82299c85e13405472394061668e1f1698 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 15:21:17 +0900 Subject: [PATCH 294/321] Add failing test covering snap calculations with SV applied --- ...tSceneHitObjectComposerDistanceSnapping.cs | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 6fb21e38b0..f759ed5dcf 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -104,6 +105,28 @@ namespace osu.Game.Tests.Editing assertSnapDistance(100f / divisor, null, true); } + /// + /// The basic distance-duration functions should always include slider velocity of the reference object. + /// + [Test] + public void TestConversionsWithSliderVelocity() + { + const float base_distance = 100; + const float slider_velocity = 1.2f; + + var referenceObject = new HitObject + { + DifficultyControlPoint = new DifficultyControlPoint + { + SliderVelocity = slider_velocity + } + }; + + assertSnapDistance(base_distance * slider_velocity, referenceObject, true); + assertSnappedDistance(base_distance * slider_velocity + 10, base_distance * slider_velocity, referenceObject); + assertSnappedDuration(base_distance * slider_velocity + 10, 1000, referenceObject); + } + [Test] public void TestConvertDurationToDistance() { @@ -210,20 +233,20 @@ namespace osu.Game.Tests.Editing assertSnappedDistance(400, 400); } - private void assertSnapDistance(float expectedDistance, HitObject? refereneObject, bool includeSliderVelocity) - => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(refereneObject ?? new HitObject(), includeSliderVelocity), () => Is.EqualTo(expectedDistance)); + private void assertSnapDistance(float expectedDistance, HitObject? referenceObject, bool includeSliderVelocity) + => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(referenceObject ?? new HitObject(), includeSliderVelocity), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON)); - private void assertDurationToDistance(double duration, float expectedDistance) - => AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(new HitObject(), duration) == expectedDistance); + private void assertDurationToDistance(double duration, float expectedDistance, HitObject? referenceObject = null) + => AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(referenceObject ?? new HitObject(), duration), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON)); - private void assertDistanceToDuration(float distance, double expectedDuration) - => AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(new HitObject(), distance) == expectedDuration); + private void assertDistanceToDuration(float distance, double expectedDuration, HitObject? referenceObject = null) + => AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDuration).Within(Precision.FLOAT_EPSILON)); - private void assertSnappedDuration(float distance, double expectedDuration) - => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.FindSnappedDuration(new HitObject(), distance) == expectedDuration); + private void assertSnappedDuration(float distance, double expectedDuration, HitObject? referenceObject = null) + => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.FindSnappedDuration(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDuration).Within(Precision.FLOAT_EPSILON)); - private void assertSnappedDistance(float distance, float expectedDistance) - => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.FindSnappedDistance(new HitObject(), distance) == expectedDistance); + private void assertSnappedDistance(float distance, float expectedDistance, HitObject? referenceObject = null) + => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.FindSnappedDistance(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON)); private class TestHitObjectComposer : OsuHitObjectComposer { From 8280605e92f6ab0d8220531a22b50895bf52ef27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 16:31:09 +0900 Subject: [PATCH 295/321] Fix notch toggle not applying correctly after restart --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2ac2edd3fc..4f8098136f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -313,7 +313,7 @@ namespace osu.Game Beatmap.BindValueChanged(beatmapChanged, true); applySafeAreaConsiderations = LocalConfig.GetBindable(OsuSetting.SafeAreaConsiderations); - applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All); + applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All, true); } private ExternalLinkOpener externalLinkOpener; From d15585153daf759289013bce8f06e32ada67a501 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 17:07:58 +0900 Subject: [PATCH 296/321] Fix breadcrumb display in directory selector overlapping new "show hidden" button Closes #21034. --- .../UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs index 0833c7eb8b..08a569269e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelectorBreadcrumbDisplay.cs @@ -25,10 +25,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName); - [BackgroundDependencyLoader] - private void load() + public OsuDirectorySelectorBreadcrumbDisplay() { - Height = 50; + Padding = new MarginPadding(15); } private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory From 29bc653d24caaf65038ee39c75b7dab1bfc5307b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 17:14:30 +0900 Subject: [PATCH 297/321] Remove incorrect double multiplication and add missing test coverage --- .../Editing/TestSceneHitObjectComposerDistanceSnapping.cs | 3 +++ osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index f759ed5dcf..495a221159 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -125,6 +125,9 @@ namespace osu.Game.Tests.Editing assertSnapDistance(base_distance * slider_velocity, referenceObject, true); assertSnappedDistance(base_distance * slider_velocity + 10, base_distance * slider_velocity, referenceObject); assertSnappedDuration(base_distance * slider_velocity + 10, 1000, referenceObject); + + assertDistanceToDuration(base_distance * slider_velocity, 1000, referenceObject); + assertDurationToDistance(1000, base_distance * slider_velocity, referenceObject); } [Test] diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 840ed7682a..b0a2694a0a 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Edit public virtual double DistanceToDuration(HitObject referenceObject, float distance) { double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime); - return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength * referenceObject.DifficultyControlPoint.SliderVelocity; + return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength; } public virtual double FindSnappedDuration(HitObject referenceObject, float distance) From 978d15955c911bf4eeef844a3eefabb71a6cb974 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 17:37:32 +0900 Subject: [PATCH 298/321] Fix control points not being cloned when running beatmap conversion Closes #20994. I haven't considered how this affects performance of difficulty calculation or otherwise. Seems like a sane initial fix which we can iterate on going forward. I've tested using the scenario in the linked issue. I'm not going to add test coverage because [BeatmapConversionTest](https://github.com/ppy/osu/blob/master/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs) should correctly cover this scenario once we are serialising control points as well. --- osu.Game/Beatmaps/BeatmapConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 4419791e43..c7c244bf0e 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -47,6 +47,7 @@ namespace osu.Game.Beatmaps // Shallow clone isn't enough to ensure we don't mutate beatmap info unexpectedly. // Can potentially be removed after `Beatmap.Difficulty` doesn't save back to `Beatmap.BeatmapInfo`. original.BeatmapInfo = original.BeatmapInfo.Clone(); + original.ControlPointInfo = original.ControlPointInfo.DeepClone(); return ConvertBeatmap(original, cancellationToken); } From 37407293aa516363a2f4290311db371bebdbd96f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 1 Nov 2022 17:57:20 +0900 Subject: [PATCH 299/321] Adjust target and hold off mod multipliers --- osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs index ca9bc89473..2b0098744f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Acronym => "HO"; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.9; public override LocalisableString Description => @"Replaces all hold notes with normal notes."; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 406968ba08..dc64918e03 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.ModTarget; public override LocalisableString Description => @"Practice keeping up with the beat of the song."; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.1; public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { From c179f0bb521bdc6c757a39fe8cff8955b5d09864 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 18:31:30 +0900 Subject: [PATCH 300/321] Fix argon hit circle outer gradient getting smaller each state application --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index 36dc8c801d..4b4860e789 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon // This is to give it a bomb-like effect, with the border "triggering" its animation when getting close. using (BeginDelayedSequence(flash_in_duration / 12)) { - outerGradient.ResizeTo(outerGradient.Size * shrink_size, resize_duration, Easing.OutElasticHalf); + outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf); outerGradient .FadeColour(Color4.White, 80) .Then() From ff60eebe216618e9f2e7ac1ca4fd114f0632c847 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 19:12:59 +0900 Subject: [PATCH 301/321] Fix large transform retention when adjusting accent colour of hitobject during pause --- .../Skinning/Argon/ArgonMainCirclePiece.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs index 36dc8c801d..7269c7b151 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonMainCirclePiece.cs @@ -121,12 +121,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f)); flash.Colour = colour.NewValue; - updateStateTransforms(drawableObject, drawableObject.State.Value); + // Accent colour may be changed many times during a paused gameplay state. + // Schedule the change to avoid transforms piling up. + Scheduler.AddOnce(updateStateTransforms); }, true); drawableObject.ApplyCustomUpdateState += updateStateTransforms; } + private void updateStateTransforms() => updateStateTransforms(drawableObject, drawableObject.State.Value); + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) From f014acfc8d54c453a50b0f9b22862f6c29204761 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 19:34:53 +0900 Subject: [PATCH 302/321] Fix `DrawableHitObject.AccentColour` not being updated if object entry is not attached --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d624164013..e5150576f2 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -266,6 +266,10 @@ namespace osu.Game.Rulesets.Objects.Drawables updateState(ArmedState.Miss, true); else updateState(ArmedState.Idle, true); + + // Combo colour may have been applied via a bindable flow while no object entry was attached. + // Update here to ensure we're in a good state. + UpdateComboColour(); } } From cd8dc9b17bbfb43e30a28ce4e765885380b494b6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 19:47:20 +0900 Subject: [PATCH 303/321] Rename "Target" to "Target Practice" --- osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs | 2 +- .../Mods/{OsuModTarget.cs => OsuModTargetPractice.cs} | 7 +++---- osu.Game.Rulesets.Osu/OsuRuleset.cs | 6 +++--- 7 files changed, 11 insertions(+), 12 deletions(-) rename osu.Game.Rulesets.Osu/Mods/{OsuModTarget.cs => OsuModTargetPractice.cs} (98%) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs index 01d83b55e6..b4727b3c02 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuLegacyModConversionTest.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests new object[] { LegacyMods.Autoplay, new[] { typeof(OsuModAutoplay) } }, new object[] { LegacyMods.SpunOut, new[] { typeof(OsuModSpunOut) } }, new object[] { LegacyMods.Autopilot, new[] { typeof(OsuModAutopilot) } }, - new object[] { LegacyMods.Target, new[] { typeof(OsuModTarget) } }, + new object[] { LegacyMods.Target, new[] { typeof(OsuModTargetPractice) } }, new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(OsuModHardRock), typeof(OsuModDoubleTime) } } }; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 618fcfe05d..1621bb50b1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override LocalisableString Description => "It never gets boring!"; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray(); [SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider))] public BindableFloat AngleSharpness { get; } = new BindableFloat(7) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 9708800daa..f691731afe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override LocalisableString Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) }; public void ApplyToDrawableHitObject(DrawableHitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index 67b19124e1..af37f1e2e5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.DifficultyIncrease; public override LocalisableString Description => @"Once you start a slider, follow precisely or get a miss."; public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) }; + public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTargetPractice) }; public void ApplyToDrawableHitObject(DrawableHitObject drawable) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index 429fe30fc5..b4edb1581e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), - typeof(OsuModTarget), + typeof(OsuModTargetPractice), }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs similarity index 98% rename from osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs index dc64918e03..55c20eebe9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs @@ -32,11 +32,10 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, - IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, - IHasSeed, IHidesApproachCircles + public class OsuModTargetPractice : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset, + IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed, IHidesApproachCircles { - public override string Name => "Target"; + public override string Name => "Target Practice"; public override string Acronym => "TP"; public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.ModTarget; diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 69df12ff6d..79a566e33c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu yield return new OsuModSpunOut(); if (mods.HasFlagFast(LegacyMods.Target)) - yield return new OsuModTarget(); + yield return new OsuModTargetPractice(); if (mods.HasFlagFast(LegacyMods.TouchDevice)) yield return new OsuModTouchDevice(); @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu value |= LegacyMods.SpunOut; break; - case OsuModTarget: + case OsuModTargetPractice: value |= LegacyMods.Target; break; @@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu case ModType.Conversion: return new Mod[] { - new OsuModTarget(), + new OsuModTargetPractice(), new OsuModDifficultyAdjust(), new OsuModClassic(), new OsuModRandom(), From e10424286448cc962a346234d0eea9ac2bad4c1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Nov 2022 22:28:06 +0900 Subject: [PATCH 304/321] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b691751f13..8711ceec64 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9dd0d18817..8d45ebec57 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 6dce938ebf..76d2e727c8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -62,7 +62,7 @@ - + @@ -82,7 +82,7 @@ - + From 46d1713e28c1726c28d998bd89a9c481cb7a53ce Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 11:43:22 +0900 Subject: [PATCH 305/321] Rename Socket* -> PersistentEndpoint* --- osu.Game/Online/HubClient.cs | 2 +- osu.Game/Online/HubClientConnector.cs | 6 +++--- .../{SocketClient.cs => PersistentEndpointClient.cs} | 2 +- ...tConnector.cs => PersistentEndpointClientConnector.cs} | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game/Online/{SocketClient.cs => PersistentEndpointClient.cs} (90%) rename osu.Game/Online/{SocketClientConnector.cs => PersistentEndpointClientConnector.cs} (95%) diff --git a/osu.Game/Online/HubClient.cs b/osu.Game/Online/HubClient.cs index 262e298f34..07428f58b4 100644 --- a/osu.Game/Online/HubClient.cs +++ b/osu.Game/Online/HubClient.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.SignalR.Client; namespace osu.Game.Online { - public class HubClient : SocketClient + public class HubClient : PersistentEndpointClient { public readonly HubConnection Connection; diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 33e9f92817..6f246f6dd3 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -14,7 +14,7 @@ using osu.Game.Online.API; namespace osu.Game.Online { - public class HubClientConnector : SocketClientConnector, IHubClientConnector + public class HubClientConnector : PersistentEndpointClientConnector, IHubClientConnector { public const string SERVER_SHUTDOWN_MESSAGE = "Server is shutting down."; @@ -51,7 +51,7 @@ namespace osu.Game.Online this.preferMessagePack = preferMessagePack; } - protected override Task BuildConnectionAsync(CancellationToken cancellationToken) + protected override Task BuildConnectionAsync(CancellationToken cancellationToken) { var builder = new HubConnectionBuilder() .WithUrl(endpoint, options => @@ -91,7 +91,7 @@ namespace osu.Game.Online ConfigureConnection?.Invoke(newConnection); - return Task.FromResult((SocketClient)new HubClient(newConnection)); + return Task.FromResult((PersistentEndpointClient)new HubClient(newConnection)); } protected override string ClientName { get; } diff --git a/osu.Game/Online/SocketClient.cs b/osu.Game/Online/PersistentEndpointClient.cs similarity index 90% rename from osu.Game/Online/SocketClient.cs rename to osu.Game/Online/PersistentEndpointClient.cs index 3b4aa1b49b..d6ed2d8aec 100644 --- a/osu.Game/Online/SocketClient.cs +++ b/osu.Game/Online/PersistentEndpointClient.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace osu.Game.Online { - public abstract class SocketClient : IAsyncDisposable + public abstract class PersistentEndpointClient : IAsyncDisposable { public event Func? Closed; diff --git a/osu.Game/Online/SocketClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs similarity index 95% rename from osu.Game/Online/SocketClientConnector.cs rename to osu.Game/Online/PersistentEndpointClientConnector.cs index 823e724ef9..fca7f2ed48 100644 --- a/osu.Game/Online/SocketClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -12,7 +12,7 @@ using osu.Game.Online.API; namespace osu.Game.Online { - public abstract class SocketClientConnector : Component + public abstract class PersistentEndpointClientConnector : Component { /// /// Whether this is connected to the hub, use to access the connection, if this is true. @@ -22,7 +22,7 @@ namespace osu.Game.Online /// /// The current connection opened by this connector. /// - public SocketClient? CurrentConnection { get; private set; } + public PersistentEndpointClient? CurrentConnection { get; private set; } private readonly Bindable isConnected = new Bindable(); private readonly SemaphoreSlim connectionLock = new SemaphoreSlim(1); @@ -34,7 +34,7 @@ namespace osu.Game.Online /// Constructs a new . /// /// An API provider used to react to connection state changes. - protected SocketClientConnector(IAPIProvider api) + protected PersistentEndpointClientConnector(IAPIProvider api) { apiState.BindTo(api.State); apiState.BindValueChanged(_ => Task.Run(connectIfPossible), true); @@ -124,7 +124,7 @@ namespace osu.Game.Online await Task.Delay(5000, cancellationToken).ConfigureAwait(false); } - protected abstract Task BuildConnectionAsync(CancellationToken cancellationToken); + protected abstract Task BuildConnectionAsync(CancellationToken cancellationToken); private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) { From c9108ce41bd3afc65a931bbd7f79a770030b75df Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 11:44:16 +0900 Subject: [PATCH 306/321] Rename StartAsync -> ConnectAsync --- osu.Game/Online/HubClient.cs | 2 +- osu.Game/Online/PersistentEndpointClient.cs | 2 +- osu.Game/Online/PersistentEndpointClientConnector.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/HubClient.cs b/osu.Game/Online/HubClient.cs index 07428f58b4..583f15a4a4 100644 --- a/osu.Game/Online/HubClient.cs +++ b/osu.Game/Online/HubClient.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online Connection.Closed += InvokeClosed; } - public override Task StartAsync(CancellationToken cancellationToken) => Connection.StartAsync(cancellationToken); + public override Task ConnectAsync(CancellationToken cancellationToken) => Connection.StartAsync(cancellationToken); public override async ValueTask DisposeAsync() { diff --git a/osu.Game/Online/PersistentEndpointClient.cs b/osu.Game/Online/PersistentEndpointClient.cs index d6ed2d8aec..e0307f7c6a 100644 --- a/osu.Game/Online/PersistentEndpointClient.cs +++ b/osu.Game/Online/PersistentEndpointClient.cs @@ -13,7 +13,7 @@ namespace osu.Game.Online protected Task InvokeClosed(Exception? exception) => Closed?.Invoke(exception) ?? Task.CompletedTask; - public abstract Task StartAsync(CancellationToken cancellationToken); + public abstract Task ConnectAsync(CancellationToken cancellationToken); public virtual ValueTask DisposeAsync() { diff --git a/osu.Game/Online/PersistentEndpointClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs index fca7f2ed48..7082757b15 100644 --- a/osu.Game/Online/PersistentEndpointClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -92,7 +92,7 @@ namespace osu.Game.Online cancellationToken.ThrowIfCancellationRequested(); - await CurrentConnection.StartAsync(cancellationToken).ConfigureAwait(false); + await CurrentConnection.ConnectAsync(cancellationToken).ConfigureAwait(false); Logger.Log($"{ClientName} connected!", LoggingTarget.Network); isConnected.Value = true; From e59c8b7d24fff0072a1392442808ad55634cb6ba Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 11:49:04 +0900 Subject: [PATCH 307/321] Use IDisposable instead --- .../PersistentEndpointClientConnector.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/PersistentEndpointClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs index 7082757b15..2ea0c95ec1 100644 --- a/osu.Game/Online/PersistentEndpointClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -6,13 +6,12 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; -using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Online.API; namespace osu.Game.Online { - public abstract class PersistentEndpointClientConnector : Component + public abstract class PersistentEndpointClientConnector : IDisposable { /// /// Whether this is connected to the hub, use to access the connection, if this is true. @@ -173,11 +172,23 @@ namespace osu.Game.Online public override string ToString() => $"{ClientName} ({(IsConnected.Value ? "connected" : "not connected")})"; - protected override void Dispose(bool isDisposing) + private bool isDisposed; + + protected virtual void Dispose(bool isDisposing) { - base.Dispose(isDisposing); + if (isDisposed) + return; + apiState.UnbindAll(); cancelExistingConnect(); + + isDisposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } } } From 30800c925299a7701e3f83853f47ee22afeb8386 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 13:15:34 +0900 Subject: [PATCH 308/321] Add/adjust xmldocs --- osu.Game/Online/PersistentEndpointClient.cs | 11 +++++++++++ osu.Game/Online/PersistentEndpointClientConnector.cs | 8 ++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/PersistentEndpointClient.cs b/osu.Game/Online/PersistentEndpointClient.cs index e0307f7c6a..32c243fbbb 100644 --- a/osu.Game/Online/PersistentEndpointClient.cs +++ b/osu.Game/Online/PersistentEndpointClient.cs @@ -9,10 +9,21 @@ namespace osu.Game.Online { public abstract class PersistentEndpointClient : IAsyncDisposable { + /// + /// An event notifying the that the connection has been closed + /// public event Func? Closed; + /// + /// Notifies the that the connection has been closed. + /// + /// The exception that the connection closed with. protected Task InvokeClosed(Exception? exception) => Closed?.Invoke(exception) ?? Task.CompletedTask; + /// + /// Connects the client to the remote service to begin processing messages. + /// + /// A cancellation token to stop processing messages. public abstract Task ConnectAsync(CancellationToken cancellationToken); public virtual ValueTask DisposeAsync() diff --git a/osu.Game/Online/PersistentEndpointClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs index 2ea0c95ec1..70e10c6c7d 100644 --- a/osu.Game/Online/PersistentEndpointClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online public abstract class PersistentEndpointClientConnector : IDisposable { /// - /// Whether this is connected to the hub, use to access the connection, if this is true. + /// Whether the managed connection is currently connected. When true use to access the connection. /// public IBindable IsConnected => isConnected; @@ -30,7 +30,7 @@ namespace osu.Game.Online private readonly IBindable apiState = new Bindable(); /// - /// Constructs a new . + /// Constructs a new . /// /// An API provider used to react to connection state changes. protected PersistentEndpointClientConnector(IAPIProvider api) @@ -123,6 +123,10 @@ namespace osu.Game.Online await Task.Delay(5000, cancellationToken).ConfigureAwait(false); } + /// + /// Creates a new . + /// + /// A cancellation token to stop the process. protected abstract Task BuildConnectionAsync(CancellationToken cancellationToken); private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) From 37300ba9e25ae1d2e3f6e9dd747d27b5fa4d18c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 13:55:01 +0900 Subject: [PATCH 309/321] Standardise "Visual Settings" components to fix mismatched paddings and labels --- .../Play/PlayerSettings/PlayerCheckbox.cs | 21 ++++++++++++------- .../Play/PlayerSettings/VisualSettings.cs | 17 ++------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerCheckbox.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerCheckbox.cs index dc09676254..cea03d2155 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerCheckbox.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerCheckbox.cs @@ -1,22 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; namespace osu.Game.Screens.Play.PlayerSettings { - public class PlayerCheckbox : OsuCheckbox + public class PlayerCheckbox : SettingsCheckbox { - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override Drawable CreateControl() => new PlayerCheckboxControl(); + + public class PlayerCheckboxControl : OsuCheckbox { - Nub.AccentColour = colours.Yellow; - Nub.GlowingAccentColour = colours.YellowLighter; - Nub.GlowColour = colours.YellowDark; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Nub.AccentColour = colours.Yellow; + Nub.GlowingAccentColour = colours.YellowLighter; + Nub.GlowColour = colours.YellowDark; + } } } } diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index e55af0bba7..bb3360acec 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; -using osu.Game.Graphics.Sprites; using osu.Game.Localisation; namespace osu.Game.Screens.Play.PlayerSettings @@ -24,26 +21,16 @@ namespace osu.Game.Screens.Play.PlayerSettings { Children = new Drawable[] { - new OsuSpriteText - { - Text = GameplaySettingsStrings.BackgroundDim - }, dimSliderBar = new PlayerSliderBar { + LabelText = GameplaySettingsStrings.BackgroundDim, DisplayAsPercentage = true }, - new OsuSpriteText - { - Text = GameplaySettingsStrings.BackgroundBlur - }, blurSliderBar = new PlayerSliderBar { + LabelText = GameplaySettingsStrings.BackgroundBlur, DisplayAsPercentage = true }, - new OsuSpriteText - { - Text = "Toggles:" - }, showStoryboardToggle = new PlayerCheckbox { LabelText = GraphicsSettingsStrings.StoryboardVideo }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapSkins }, beatmapColorsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapColours }, From e761c0395d411aadc40e565f8deeff43c641c4a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 14:47:56 +0900 Subject: [PATCH 310/321] Fix multiple notifications arriving for imports in edge cases --- osu.Game/Overlays/Notifications/ProgressNotification.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 4cf47013bd..7b62c95179 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Notifications } } - private bool completionSent; + private int completionSent; /// /// Attempt to post a completion notification. @@ -162,11 +162,11 @@ namespace osu.Game.Overlays.Notifications if (CompletionTarget == null) return; - if (completionSent) + // Thread-safe barrier, as this may be called by a web request and also scheduled to the update thread at the same time. + if (Interlocked.Increment(ref completionSent) == 0) return; CompletionTarget.Invoke(CreateCompletionNotification()); - completionSent = true; Close(false); } From e5f53b1ad8346c3d0e9241bfd79138df2aae3121 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 15:18:47 +0900 Subject: [PATCH 311/321] Use Interlocked.Exhange() instead Increment isn't correct since it returns the post-incremented value. It also always increments. --- osu.Game/Overlays/Notifications/ProgressNotification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 7b62c95179..54a1d69a9e 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.Notifications return; // Thread-safe barrier, as this may be called by a web request and also scheduled to the update thread at the same time. - if (Interlocked.Increment(ref completionSent) == 0) + if (Interlocked.Exchange(ref completionSent, 1) == 0) return; CompletionTarget.Invoke(CreateCompletionNotification()); From db34f238c09597748ff5c9e6c22211d8b8346641 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 15:47:30 +0900 Subject: [PATCH 312/321] Fix inverted condition --- osu.Game/Overlays/Notifications/ProgressNotification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 54a1d69a9e..9812feb4a1 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.Notifications return; // Thread-safe barrier, as this may be called by a web request and also scheduled to the update thread at the same time. - if (Interlocked.Exchange(ref completionSent, 1) == 0) + if (Interlocked.Exchange(ref completionSent, 1) == 1) return; CompletionTarget.Invoke(CreateCompletionNotification()); From c66064872a18d1bd75cdcdde80fa4242f5054978 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 15:45:23 +0900 Subject: [PATCH 313/321] Fix `DrawableHit` test scene not showing rim hits correctly --- .../Skinning/TestSceneDrawableHit.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 8e9c487c2f..eb2b6c1d74 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -30,23 +30,24 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Origin = Anchor.Centre, })); - AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) + AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(rim: true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true)) + AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true, true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); } - private Hit createHitAtCurrentTime(bool strong = false) + private Hit createHitAtCurrentTime(bool strong = false, bool rim = false) { var hit = new Hit { + Type = rim ? HitType.Rim : HitType.Centre, IsStrong = strong, StartTime = Time.Current + 3000, }; From 0689d3845ff82fcae11f5e35e65f72b0e1e6be7a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 16:55:17 +0900 Subject: [PATCH 314/321] Fix `TestSceneHitExplosion` not maintaining aspect ratio --- .../Skinning/TestSceneHitExplosion.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index f87e0355ad..0ddc607336 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Taiko.Tests.Skinning { @@ -49,11 +50,19 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning // the hit needs to be added to hierarchy in order for nested objects to be created correctly. // setting zero alpha is supposed to prevent the test from looking broken. hit.With(h => h.Alpha = 0), - new HitExplosion(hit.Type) + + new AspectContainer { + RelativeSizeAxes = Axes.X, Anchor = Anchor.Centre, Origin = Anchor.Centre, - }.With(explosion => explosion.Apply(hit)) + Child = + new HitExplosion(hit.Type) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }.With(explosion => explosion.Apply(hit)) + } } }; } From 9c758e542505adc7e64049993d843e359c0c0410 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 17:00:39 +0900 Subject: [PATCH 315/321] Move `DefaultInputDrum` to correct location --- .../Skinning/Default/DefaultInputDrum.cs | 181 ++++++++++++++++++ osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 172 +---------------- 2 files changed, 182 insertions(+), 171 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/Default/DefaultInputDrum.cs diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultInputDrum.cs new file mode 100644 index 0000000000..fa60d209e7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultInputDrum.cs @@ -0,0 +1,181 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Screens.Ranking; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.Skinning.Default +{ + public class DefaultInputDrum : AspectContainer + { + public DefaultInputDrum() + { + RelativeSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load() + { + const float middle_split = 0.025f; + + InternalChild = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.9f), + Children = new[] + { + new TaikoHalfDrum(false) + { + Name = "Left Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = -middle_split / 2, + RimAction = TaikoAction.LeftRim, + CentreAction = TaikoAction.LeftCentre + }, + new TaikoHalfDrum(true) + { + Name = "Right Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + RelativePositionAxes = Axes.X, + X = middle_split / 2, + RimAction = TaikoAction.RightRim, + CentreAction = TaikoAction.RightCentre + } + } + }; + } + + /// + /// A half-drum. Contains one centre and one rim hit. + /// + private class TaikoHalfDrum : Container, IKeyBindingHandler + { + /// + /// The key to be used for the rim of the half-drum. + /// + public TaikoAction RimAction; + + /// + /// The key to be used for the centre of the half-drum. + /// + public TaikoAction CentreAction; + + private readonly Sprite rim; + private readonly Sprite rimHit; + private readonly Sprite centre; + private readonly Sprite centreHit; + + public TaikoHalfDrum(bool flipped) + { + Masking = true; + + Children = new Drawable[] + { + rim = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both + }, + rimHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Blending = BlendingParameters.Additive, + }, + centre = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f) + }, + centreHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f), + Alpha = 0, + Blending = BlendingParameters.Additive + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit"); + + rimHit.Colour = colours.Blue; + centreHit.Colour = colours.Pink; + } + + public bool OnPressed(KeyBindingPressEvent e) + { + Drawable target = null; + Drawable back = null; + + if (e.Action == CentreAction) + { + target = centreHit; + back = centre; + } + else if (e.Action == RimAction) + { + target = rimHit; + back = rim; + } + + if (target != null) + { + const float scale_amount = 0.05f; + const float alpha_amount = 0.5f; + + const float down_time = 40; + const float up_time = 1000; + + back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint) + .Then() + .ScaleTo(1, up_time, Easing.OutQuint); + + target.Animate( + t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint), + t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint) + ).Then( + t => t.ScaleTo(1, up_time, Easing.OutQuint), + t => t.FadeOut(up_time, Easing.OutQuint) + ); + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 054f98e18f..94e7138142 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -3,18 +3,11 @@ #nullable disable -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Screens.Ranking; +using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Taiko.UI { @@ -23,8 +16,6 @@ namespace osu.Game.Rulesets.Taiko.UI /// internal class InputDrum : Container { - private const float middle_split = 0.025f; - public InputDrum() { AutoSizeAxes = Axes.X; @@ -43,166 +34,5 @@ namespace osu.Game.Rulesets.Taiko.UI }, }; } - - private class DefaultInputDrum : AspectContainer - { - public DefaultInputDrum() - { - RelativeSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Scale = new Vector2(0.9f), - Children = new[] - { - new TaikoHalfDrum(false) - { - Name = "Left Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = -middle_split / 2, - RimAction = TaikoAction.LeftRim, - CentreAction = TaikoAction.LeftCentre - }, - new TaikoHalfDrum(true) - { - Name = "Right Half", - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.X, - X = middle_split / 2, - RimAction = TaikoAction.RightRim, - CentreAction = TaikoAction.RightCentre - } - } - }; - } - - /// - /// A half-drum. Contains one centre and one rim hit. - /// - private class TaikoHalfDrum : Container, IKeyBindingHandler - { - /// - /// The key to be used for the rim of the half-drum. - /// - public TaikoAction RimAction; - - /// - /// The key to be used for the centre of the half-drum. - /// - public TaikoAction CentreAction; - - private readonly Sprite rim; - private readonly Sprite rimHit; - private readonly Sprite centre; - private readonly Sprite centreHit; - - public TaikoHalfDrum(bool flipped) - { - Masking = true; - - Children = new Drawable[] - { - rim = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both - }, - rimHit = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Blending = BlendingParameters.Additive, - }, - centre = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f) - }, - centreHit = new Sprite - { - Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f), - Alpha = 0, - Blending = BlendingParameters.Additive - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures, OsuColour colours) - { - rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer"); - rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit"); - centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner"); - centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit"); - - rimHit.Colour = colours.Blue; - centreHit.Colour = colours.Pink; - } - - public bool OnPressed(KeyBindingPressEvent e) - { - Drawable target = null; - Drawable back = null; - - if (e.Action == CentreAction) - { - target = centreHit; - back = centre; - } - else if (e.Action == RimAction) - { - target = rimHit; - back = rim; - } - - if (target != null) - { - const float scale_amount = 0.05f; - const float alpha_amount = 0.5f; - - const float down_time = 40; - const float up_time = 1000; - - back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint) - .Then() - .ScaleTo(1, up_time, Easing.OutQuint); - - target.Animate( - t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint), - t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint) - ).Then( - t => t.ScaleTo(1, up_time, Easing.OutQuint), - t => t.FadeOut(up_time, Easing.OutQuint) - ); - } - - return false; - } - - public void OnReleased(KeyBindingReleaseEvent e) - { - } - } - } } } From 2407eb063d4850f23d29b9fa9364722a7a84e744 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 16:51:55 +0900 Subject: [PATCH 316/321] Simplify `Circle` usage in default `CentreHitCirclePiece` --- .../Skinning/Default/CentreHitCirclePiece.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs index b91d5cfe8d..958f4b3a17 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CentreHitCirclePiece.cs @@ -41,12 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default Children = new[] { - new CircularContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] { new Box { RelativeSizeAxes = Axes.Both } } - } + new Circle { RelativeSizeAxes = Axes.Both } }; } } From 910dd3ad01ed44be94c0bc5503134b214e19bc08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 17:04:45 +0900 Subject: [PATCH 317/321] Move more default classes to correct namespace --- .../Default}/DefaultHitExplosion.cs | 14 ++++++-------- .../Default}/DefaultKiaiHitExplosion.cs | 6 ++---- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 1 + osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 1 + 4 files changed, 10 insertions(+), 12 deletions(-) rename osu.Game.Rulesets.Taiko/{UI => Skinning/Default}/DefaultHitExplosion.cs (87%) rename osu.Game.Rulesets.Taiko/{UI => Skinning/Default}/DefaultKiaiHitExplosion.cs (96%) diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs similarity index 87% rename from osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs rename to osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs index 687c8f788f..58f45fda7c 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs @@ -1,9 +1,7 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,19 +10,19 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; using osuTK.Graphics; -namespace osu.Game.Rulesets.Taiko.UI +namespace osu.Game.Rulesets.Taiko.Skinning.Default { internal class DefaultHitExplosion : CircularContainer, IAnimatableHitExplosion { private readonly HitResult result; - [CanBeNull] - private Box body; + private Box? body; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; public DefaultHitExplosion(HitResult result) { @@ -58,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.UI updateColour(); } - private void updateColour([CanBeNull] DrawableHitObject judgedObject = null) + private void updateColour(DrawableHitObject? judgedObject = null) { if (body == null) return; diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultKiaiHitExplosion.cs similarity index 96% rename from osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs rename to osu.Game.Rulesets.Taiko/Skinning/Default/DefaultKiaiHitExplosion.cs index e91475d87b..ae68d63d97 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultKiaiHitExplosion.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,8 +8,9 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Rulesets.Taiko.Objects; +using osuTK; -namespace osu.Game.Rulesets.Taiko.UI +namespace osu.Game.Rulesets.Taiko.Skinning.Default { public class DefaultKiaiHitExplosion : CircularContainer { diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 046b3a6fd0..d8cdf50d75 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 319d8979ae..36d2e32984 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Skinning; using osuTK; From bc3382f373c83091d316b73c45cc9919952c68e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 17:07:19 +0900 Subject: [PATCH 318/321] Apply NRT to many taiko classes --- .../Skinning/Default/ElongatedCirclePiece.cs | 2 -- .../Skinning/Default/RimHitCirclePiece.cs | 2 -- .../Skinning/Default/SwellSymbolPiece.cs | 2 -- .../Skinning/Default/TickPiece.cs | 2 -- .../Skinning/Legacy/LegacyBarLine.cs | 2 -- .../Skinning/Legacy/LegacyCirclePiece.cs | 6 ++---- .../Skinning/Legacy/LegacyDrumRoll.cs | 8 +++----- .../Skinning/Legacy/LegacyHit.cs | 2 -- .../Skinning/Legacy/LegacyHitExplosion.cs | 9 +++------ .../Skinning/Legacy/LegacyInputDrum.cs | 10 ++++------ .../Skinning/Legacy/LegacyTaikoScroller.cs | 8 +++----- .../Skinning/Legacy/TaikoLegacyHitTarget.cs | 4 +--- .../Legacy/TaikoLegacyPlayfieldBackgroundRight.cs | 4 +--- osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs | 2 -- osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs | 2 -- osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs | 2 -- .../UI/DrawableTaikoJudgement.cs | 2 -- osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs | 7 +++---- osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs | 2 -- .../UI/DrumSampleTriggerSource.cs | 2 -- osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 13 +++++-------- osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs | 2 -- .../UI/IAnimatableHitExplosion.cs | 2 -- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 2 -- osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 4 +--- .../UI/PlayfieldBackgroundLeft.cs | 2 -- .../UI/PlayfieldBackgroundRight.cs | 2 -- osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs | 2 -- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 6 ++---- .../UI/TaikoMascotAnimationState.cs | 2 -- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 2 -- osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs | 2 -- 32 files changed, 28 insertions(+), 93 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs index ba2679fe97..210841bca0 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/ElongatedCirclePiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs index 63269f1267..c6165495d8 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/RimHitCirclePiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs index d19dc4c887..2f59cac3ff 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/SwellSymbolPiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs index 7d3268f777..09c8243aac 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/TickPiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs index 97e0a340dd..2b528ae8ce 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyBarLine.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs index 6bbeb0ed4c..6b2576a564 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyCirclePiece.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; @@ -19,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class LegacyCirclePiece : CompositeDrawable, IHasAccentColour { - private Drawable backgroundLayer; + private Drawable backgroundLayer = null!; // required for editor blueprints (not sure why these circle pieces are zero size). public override Quad ScreenSpaceDrawQuad => backgroundLayer.ScreenSpaceDrawQuad; @@ -32,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin, DrawableHitObject drawableHitObject) { - Drawable getDrawableFor(string lookup) + Drawable? getDrawableFor(string lookup) { const string normal_hit = "taikohit"; const string big_hit = "taikobig"; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs index 040d8ff965..1249231d92 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyDrumRoll.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -28,11 +26,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } } - private LegacyCirclePiece headCircle; + private LegacyCirclePiece headCircle = null!; - private Sprite body; + private Sprite body = null!; - private Sprite tailCircle; + private Sprite tailCircle = null!; public LegacyDrumRoll() { diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs index b4277f86bb..d93317f0e2 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHit.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Game.Skinning; using osuTK.Graphics; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index 87ed2e2e60..ea45b69999 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -1,9 +1,7 @@ +#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; @@ -17,8 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { private readonly Drawable sprite; - [CanBeNull] - private readonly Drawable strongSprite; + private readonly Drawable? strongSprite; /// /// Creates a new legacy hit explosion. @@ -29,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy /// /// The normal legacy explosion sprite. /// The strong legacy explosion sprite. - public LegacyHitExplosion(Drawable sprite, [CanBeNull] Drawable strongSprite = null) + public LegacyHitExplosion(Drawable sprite, Drawable? strongSprite = null) { this.sprite = sprite; this.strongSprite = strongSprite; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 101f70b97a..0abb365750 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -20,9 +18,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy /// internal class LegacyInputDrum : Container { - private Container content; - private LegacyHalfDrum left; - private LegacyHalfDrum right; + private Container content = null!; + private LegacyHalfDrum left = null!; + private LegacyHalfDrum right = null!; public LegacyInputDrum() { @@ -142,7 +140,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy public bool OnPressed(KeyBindingPressEvent e) { - Drawable target = null; + Drawable? target = null; if (e.Action == CentreAction) { diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs index bd4a2f8935..4a2426bff5 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } [BackgroundDependencyLoader(true)] - private void load(GameplayState gameplayState) + private void load(GameplayState? gameplayState) { if (gameplayState != null) ((IBindable)LastResult).BindTo(gameplayState.LastJudgementResult); @@ -91,8 +89,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy private class ScrollerSprite : CompositeDrawable { - private Sprite passingSprite; - private Sprite failingSprite; + private Sprite passingSprite = null!; + private Sprite failingSprite = null!; private bool passing = true; diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs index a48cdf47f6..21102f6eec 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class TaikoLegacyHitTarget : CompositeDrawable { - private Container content; + private Container content = null!; [BackgroundDependencyLoader] private void load(ISkinSource skin) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs index f425a410a4..3186f615a7 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyPlayfieldBackgroundRight.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Graphics; @@ -16,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public class TaikoLegacyPlayfieldBackgroundRight : BeatSyncedContainer { - private Sprite kiai; + private Sprite kiai = null!; private bool kiaiDisplayed; diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs index 63314a6822..30bfb605aa 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponent.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index d231dc7e4f..bf48898dd2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Rulesets.Taiko { public enum TaikoSkinComponents diff --git a/osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs b/osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs index 071808a044..cb878e8ea0 100644 --- a/osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 264e4db54e..876fa207bf 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 8bedca19d8..dd0b61cdf5 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -24,7 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly Bindable LastResult; private readonly Dictionary animations; - private TaikoMascotAnimation currentAnimation; + + private TaikoMascotAnimation? currentAnimation; private bool lastObjectHit = true; private bool kiaiMode; @@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader(true)] - private void load(GameplayState gameplayState) + private void load(GameplayState? gameplayState) { InternalChildren = new[] { diff --git a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs index e0d5a3c680..ae37840825 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; diff --git a/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs index ef5bd1d7f0..3279d128d3 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumSampleTriggerSource.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Game.Audio; using osu.Game.Rulesets.Taiko.Objects; diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index d8cdf50d75..4836984eac 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -1,10 +1,8 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +#nullable enable +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; -using JetBrains.Annotations; using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -30,10 +28,9 @@ namespace osu.Game.Rulesets.Taiko.UI private double? secondHitTime; - [CanBeNull] - public DrawableHitObject JudgedObject; + public DrawableHitObject? JudgedObject; - private SkinnableDrawable skinnable; + private SkinnableDrawable skinnable = null!; /// /// This constructor only exists to meet the new() type constraint of . @@ -63,7 +60,7 @@ namespace osu.Game.Rulesets.Taiko.UI skinnable.OnSkinChanged += runAnimation; } - public void Apply([CanBeNull] DrawableHitObject drawableHitObject) + public void Apply(DrawableHitObject? drawableHitObject) { JudgedObject = drawableHitObject; secondHitTime = null; diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs index 8707f7e840..badf34554c 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosionPool.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Scoring; diff --git a/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs index 6a9d43a0ab..cf0f5f9fb6 100644 --- a/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/IAnimatableHitExplosion.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.UI diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 94e7138142..6d5b6c5f5d 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 36d2e32984..c4cff00d2a 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -23,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly HitType hitType; - private SkinnableDrawable skinnable; + private SkinnableDrawable skinnable = null!; public override double LifetimeStart => skinnable.Drawable.LifetimeStart; diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs index db1094e100..2a8890a95d 100644 --- a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs index 43252e2e77..44bfdacf37 100644 --- a/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs index f48ed2c941..6401c6d09f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoHitTarget.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 26a37fc464..de539b3cf0 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -120,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(ISkinSource source) { - ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null); + ISkin? skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null); if (skin == null) return; @@ -137,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } - private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex) + private static Texture? getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex) { var texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}{frameIndex}"); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs index 717f0d725a..02bf245b7b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimationState.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Rulesets.Taiko.UI { public enum TaikoMascotAnimationState diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index 8e99a82b1b..9cf530e903 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Bindables; using osu.Framework.Graphics; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs index a76adc495d..e6391d1386 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Replays; From 5dfaf277229566c930d0cbedc9d970d0c2b3c507 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 2 Nov 2022 17:23:45 +0900 Subject: [PATCH 319/321] A bit more cleanup --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs | 1 - osu.Game.Rulesets.Taiko/UI/HitExplosion.cs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs index ea45b69999..ff1546381b 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyHitExplosion.cs @@ -1,4 +1,3 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index 4836984eac..10a7495c62 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -1,5 +1,4 @@ -#nullable enable -// 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. using System; From bfa5d41d89a64ec1a559cc4d5cc5975563d95335 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Nov 2022 17:39:45 +0900 Subject: [PATCH 320/321] Fix one more case --- osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs index 58f45fda7c..b7ba76effa 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/DefaultHitExplosion.cs @@ -1,4 +1,3 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. From 993439d30b173a5bdac0e22fea97c0e97bcb09a7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 3 Nov 2022 11:28:39 +0900 Subject: [PATCH 321/321] Fix missed nullability --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index de539b3cf0..0f214b8436 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load(ISkinSource source) { - ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null); + ISkin? skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null); if (skin == null) return;