From 52a243afcabde3ccc375aaf6e79a9f50c046f54b Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Wed, 25 Aug 2021 21:42:15 +0300 Subject: [PATCH 001/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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/223] 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 e0904e263f4c976e24277c19f380650fb025499f Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Sun, 9 Oct 2022 11:25:47 +0200 Subject: [PATCH 030/223] 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 031/223] 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 0a0d6993084ce9f2a9010eea9a00a45c2f03df31 Mon Sep 17 00:00:00 2001 From: sw1tchbl4d3 Date: Tue, 11 Oct 2022 22:43:47 +0200 Subject: [PATCH 032/223] 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 033/223] 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 034/223] 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 035/223] 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 f67f6cc99ca914b152a355122589900437b6b91d Mon Sep 17 00:00:00 2001 From: "D.Headley" Date: Wed, 12 Oct 2022 19:45:08 +0200 Subject: [PATCH 036/223] 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 338115ff6a00d4eaf9ce90e061c71f9504307174 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 13 Oct 2022 15:05:15 +0900 Subject: [PATCH 037/223] 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 49d5931022de62351712c71d7c53789c2bc45ac1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 Oct 2022 18:39:25 +0900 Subject: [PATCH 038/223] 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 039/223] 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 040/223] 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 041/223] 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 042/223] 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 043/223] 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 044/223] 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 045/223] 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 046/223] 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 047/223] 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 048/223] 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 049/223] 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 050/223] 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 051/223] 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 e1a8bfa135a103540ad232555fd7f7f33e3eee45 Mon Sep 17 00:00:00 2001 From: Dario Headley Date: Tue, 18 Oct 2022 13:39:40 +0200 Subject: [PATCH 052/223] 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 053/223] 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 054/223] 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 055/223] 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 a76a0397226b844e923b40e3965a4e745bc0c321 Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Tue, 18 Oct 2022 23:40:43 +0200 Subject: [PATCH 056/223] 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 057/223] 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 058/223] 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 059/223] 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 060/223] 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 061/223] 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 062/223] 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 830b92d3aebb8a89298504d649baedd27f5e28e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 20:06:39 +0900 Subject: [PATCH 063/223] 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 064/223] 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 065/223] 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 066/223] 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 42bb9dc3e3db17b9cb39da3103ce10820a683db9 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Thu, 20 Oct 2022 22:17:26 +0900 Subject: [PATCH 067/223] 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 27ce3d8342e0a239042f1402ca486e44461770c6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 Oct 2022 01:52:19 +0300 Subject: [PATCH 068/223] 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 069/223] 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 6cdfddea6261a293bf54f285ead6c012eddb1cb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 16:10:55 +0900 Subject: [PATCH 070/223] 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 071/223] 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 072/223] 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 073/223] 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 1e09a212791de25c93c7f48183aacff6f369aa46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 19:55:58 +0900 Subject: [PATCH 074/223] 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 7dc03097ffa73297def50e96b8c7609db4ee1246 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 21:51:17 +0900 Subject: [PATCH 075/223] 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 076/223] 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 077/223] 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 078/223] 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 079/223] 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 080/223] 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 a74e873b983678993ca7d3c4c6d20468d052e9b7 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 23 Oct 2022 01:13:29 +0900 Subject: [PATCH 081/223] 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 082/223] 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 083/223] 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 084/223] 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 889c2978d7a95ea864c584db8bd61d81dc615f04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Oct 2022 13:13:40 +0900 Subject: [PATCH 085/223] 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 086/223] 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 087/223] 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 088/223] 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 089/223] 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 090/223] 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 091/223] 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 092/223] 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 093/223] 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 094/223] 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 095/223] 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 096/223] 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 097/223] 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 098/223] 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 099/223] 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 100/223] 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 101/223] 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 102/223] 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 103/223] 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 104/223] 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 105/223] 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 106/223] 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 107/223] 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 108/223] 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 109/223] 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 110/223] 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 111/223] 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 112/223] 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 113/223] 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 114/223] 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 115/223] 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 116/223] 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 117/223] 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 118/223] 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 119/223] 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 120/223] 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 121/223] 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 122/223] 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 123/223] 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 124/223] 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 125/223] 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 126/223] 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 127/223] 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 128/223] 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 129/223] 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 130/223] 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 131/223] 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 132/223] 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 133/223] 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 134/223] 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 135/223] 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 136/223] 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 137/223] 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 138/223] 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 139/223] 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 140/223] 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 141/223] 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 142/223] 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 143/223] 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 144/223] 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 145/223] 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 146/223] 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 147/223] 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 148/223] 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 149/223] 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 150/223] 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 151/223] 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 152/223] 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 153/223] 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 154/223] 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 155/223] 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 156/223] 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 157/223] 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 158/223] 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 159/223] 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 160/223] 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 161/223] 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 162/223] 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 163/223] 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 164/223] 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 165/223] 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 166/223] 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 167/223] 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 168/223] 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 169/223] 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 170/223] 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 171/223] 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 172/223] 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 173/223] 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 174/223] 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 175/223] 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 176/223] 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 177/223] 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 178/223] 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 179/223] 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 180/223] 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 181/223] 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 182/223] 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 183/223] 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 184/223] 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 185/223] 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 186/223] 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 187/223] 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 188/223] 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 189/223] 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 190/223] 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 191/223] 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 192/223] 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 193/223] 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 194/223] 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 195/223] 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 196/223] 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 197/223] 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 198/223] 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 199/223] 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 200/223] 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 201/223] 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 202/223] 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 203/223] 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 204/223] 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 205/223] 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 206/223] 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 207/223] 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 208/223] 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 209/223] 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 210/223] 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 211/223] 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 212/223] 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 213/223] 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 214/223] 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 215/223] 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 216/223] 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 217/223] 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 218/223] 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 219/223] 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 220/223] 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 221/223] 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 222/223] 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 223/223] 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;