From 323e4ac26b8172865bc6fbede9c39fb05ee5da40 Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Sun, 21 Feb 2021 18:24:27 +1100 Subject: [PATCH 001/165] Refactor catch Movement skill to not require explicit clockrate usage In catch, rate adjustment mods do not only affect the timings of hitobjects, but also the speed of the player's catcher. This catcher speed change has an impact on difficulty which is currently accounted for by using the clockrate directly in calculations. Semantically this is a bad idea because clockrate adjustments are supposed to be fully accounted for in DifficultyHitObjects, but passing clockrate here for the purpose of being used as catcher speed doesn't make much sense, especially since it is copied in every DifficultyHitObject despite being the same value. It makes more sense to account for this catch specific impact by handling rate adjustment mods in a catch specific way, or more specifically in a Movement skill specific way. --- .../Preprocessing/CatchDifficultyHitObject.cs | 3 --- .../Difficulty/Skills/Movement.cs | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index d936ef97ac..e19098c580 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; - public readonly double ClockRate; - public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) : base(hitObject, lastObject, clockRate) { @@ -37,7 +35,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure StrainTime = Math.Max(40, DeltaTime); - ClockRate = clockRate; } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 9ad719be1a..7d61be7bb1 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -26,10 +27,20 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills private float lastDistanceMoved; private double lastStrainTime; + /// + /// The speed multiplier applied to the player's catcher. + /// + private readonly double catcherSpeedMultiplier; + public Movement(Mod[] mods, float halfCatcherWidth) : base(mods) { HalfCatcherWidth = halfCatcherWidth; + + // In catch, rate adjustment mods do not only affect the timings of hitobjects, + // but also the speed of the player's catcher, which has an impact on difficulty + var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); + catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; } protected override double StrainValueOf(DifficultyHitObject current) @@ -46,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills float distanceMoved = playerPosition - lastPlayerPosition.Value; - double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate); + double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier); double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510); double sqrtStrain = Math.Sqrt(weightedStrainTime); @@ -79,7 +90,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills playerPosition = catchCurrent.NormalizedPosition; } - distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values + distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values } lastPlayerPosition = playerPosition; From 5117c51105419a3d48774b86d4a04b893e0cc47f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 19 Apr 2021 16:24:47 +0700 Subject: [PATCH 002/165] initial wiki header --- .../Visual/Online/TestSceneWikiHeader.cs | 25 +++++++++++++++ osu.Game/Overlays/Wiki/WikiHeader.cs | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs create mode 100644 osu.Game/Overlays/Wiki/WikiHeader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs new file mode 100644 index 0000000000..51d8abc516 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiHeader : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange); + + public TestSceneWikiHeader() + { + Child = new WikiHeader + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + } +} diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs new file mode 100644 index 0000000000..91377c63da --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiHeader : BreadcrumbControlOverlayHeader + { + private const string index_page_string = "index"; + + public WikiHeader() + { + TabControl.AddItem(index_page_string); + } + + protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); + + protected override OverlayTitle CreateTitle() => new WikiHeaderTitle(); + + private class WikiHeaderTitle : OverlayTitle + { + public WikiHeaderTitle() + { + Title = "wiki"; + Description = "knowledge base"; + IconTexture = "Icons/Hexacons/wiki"; + } + } + } +} From 460d656a0e504ec85b2da9be90d4b89fbc050e15 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 21 Apr 2021 16:21:07 +0700 Subject: [PATCH 003/165] initial wiki overlay --- .../Visual/Online/TestSceneWikiOverlay.cs | 22 +++++++++++++++++++ osu.Game/Overlays/WikiOverlay.cs | 17 ++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs create mode 100644 osu.Game/Overlays/WikiOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs new file mode 100644 index 0000000000..737c97c0bd --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.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.Overlays; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiOverlay : OsuTestScene + { + private WikiOverlay wiki; + + [SetUp] + public void SetUp() => Schedule(() => Child = wiki = new WikiOverlay()); + + [Test] + public void TestOverlay() + { + AddStep("Show", () => wiki.Show()); + } + } +} diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs new file mode 100644 index 0000000000..7105fbf953 --- /dev/null +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -0,0 +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 osu.Game.Overlays.Wiki; + +namespace osu.Game.Overlays +{ + public class WikiOverlay : OnlineOverlay + { + public WikiOverlay() + : base(OverlayColourScheme.Orange, false) + { + } + + protected override WikiHeader CreateHeader() => new WikiHeader(); + } +} From f3555ad08cfdc71a61efeb88fde64104484bf8e5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 20 Apr 2021 16:12:32 +0700 Subject: [PATCH 004/165] add APIWikiPage response --- .../API/Requests/Responses/APIWikiPage.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game/Online/API/Requests/Responses/APIWikiPage.cs diff --git a/osu.Game/Online/API/Requests/Responses/APIWikiPage.cs b/osu.Game/Online/API/Requests/Responses/APIWikiPage.cs new file mode 100644 index 0000000000..957396b17a --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIWikiPage.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 System.Collections.Generic; +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIWikiPage + { + [JsonProperty("layout")] + public string Layout { get; set; } + + [JsonProperty("locale")] + public string Locale { get; set; } + + [JsonProperty("markdown")] + public string Markdown { get; set; } + + [JsonProperty("path")] + public string Path { get; set; } + + [JsonProperty("subtitle")] + public string Subtitle { get; set; } + + [JsonProperty("tags")] + public List Tags { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + } +} From d4013bd8852aaaa833004969037089477279cfd6 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 20 Apr 2021 16:40:30 +0700 Subject: [PATCH 005/165] add GetWikiRequest --- .../Online/API/Requests/GetWikiRequest.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetWikiRequest.cs diff --git a/osu.Game/Online/API/Requests/GetWikiRequest.cs b/osu.Game/Online/API/Requests/GetWikiRequest.cs new file mode 100644 index 0000000000..248fcc03e3 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetWikiRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetWikiRequest : APIRequest + { + private readonly string path; + private readonly string locale; + + public GetWikiRequest(string path, string locale = "en") + { + this.path = path; + this.locale = locale; + } + + protected override string Target => $"wiki/{locale}/{path}"; + } +} From f6a088e699854eb449047410e6ef6835ea79451a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 19 May 2021 19:09:03 +0700 Subject: [PATCH 006/165] add request logic in wiki overlay --- osu.Game/Overlays/WikiOverlay.cs | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 7105fbf953..964daa3368 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -1,17 +1,107 @@ // 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Wiki; +using osu.Game.Overlays.Wiki.Markdown; namespace osu.Game.Overlays { public class WikiOverlay : OnlineOverlay { + private const string index_path = "Main_Page"; + + private readonly Bindable path = new Bindable(index_path); + + [Cached] + private readonly Bindable wikiData = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + private GetWikiRequest request; + + private CancellationTokenSource cancellationToken; + + private bool displayUpdateRequired = true; + public WikiOverlay() : base(OverlayColourScheme.Orange, false) { } + private void onPathChanged(ValueChangedEvent e) + { + cancellationToken?.Cancel(); + request?.Cancel(); + + request = new GetWikiRequest(e.NewValue); + + Loading.Show(); + + request.Success += response => Schedule(() => onSuccess(response)); + + api.PerformAsync(request); + } + + private void onSuccess(APIWikiPage response) + { + wikiData.Value = response; + LoadDisplay(new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = $"{path.Value}/", + Text = response.Markdown, + }); + } + protected override WikiHeader CreateHeader() => new WikiHeader(); + + protected override void LoadComplete() + { + base.LoadComplete(); + path.BindValueChanged(onPathChanged); + } + + protected override void PopIn() + { + base.PopIn(); + + if (displayUpdateRequired) + { + path.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + displayUpdateRequired = true; + } + + protected void LoadDisplay(Drawable display) + { + ScrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + Child = loaded; + Loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void Dispose(bool isDisposing) + { + cancellationToken?.Cancel(); + request?.Cancel(); + base.Dispose(isDisposing); + } } } From 416e08ae7af79700be96fdb5e1070cd0f5d6b9db Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 21 Apr 2021 17:25:13 +0700 Subject: [PATCH 007/165] add dummy response API in TestSceneWikiOverlay `TestSceneNewsOverlay` is used as example for this test. --- .../Visual/Online/TestSceneWikiOverlay.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 737c97c0bd..371be8a003 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -2,21 +2,51 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneWikiOverlay : OsuTestScene { + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + private WikiOverlay wiki; [SetUp] public void SetUp() => Schedule(() => Child = wiki = new WikiOverlay()); [Test] - public void TestOverlay() + public void TestMainPage() { - AddStep("Show", () => wiki.Show()); + setUpNewsResponse(responseExample); + AddStep("Show Main Page", () => wiki.Show()); } + + private void setUpNewsResponse(APIWikiPage r) + => AddStep("set up response", () => + { + dummyAPI.HandleRequest = request => + { + if (!(request is GetWikiRequest getWikiRequest)) + return false; + + getWikiRequest.TriggerSuccess(r); + return true; + }; + }); + + // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + private APIWikiPage responseExample => new APIWikiPage + { + Title = "Main Page", + Layout = "main_page", + Path = "Main_Page", + Locale = "en", + Subtitle = null, + Markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + }; } } From 25f2c582e7f6ab9d54fba269c26d656bef1dcb97 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 22 Apr 2021 16:15:20 +0700 Subject: [PATCH 008/165] add ToolbarWikiButton --- .../Overlays/Toolbar/ToolbarWikiButton.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs diff --git a/osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs b/osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs new file mode 100644 index 0000000000..a521219b4f --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarWikiButton.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarWikiButton : ToolbarOverlayToggleButton + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + + [BackgroundDependencyLoader(true)] + private void load(WikiOverlay wiki) + { + StateContainer = wiki; + } + } +} From 004cd7c8344162cae74380a12605b3d54a7b6a9a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 22 Apr 2021 16:16:12 +0700 Subject: [PATCH 009/165] add wiki button in main toolbar --- osu.Game/OsuGame.cs | 5 ++++- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b7946f1c2f..42a49aa65e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -81,6 +81,8 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; + private WikiOverlay wikiOverlay; + private SkinEditorOverlay skinEditor; private Container overlayContent; @@ -719,6 +721,7 @@ namespace osu.Game var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); + loadComponentSingleFile(wikiOverlay = new WikiOverlay(), overlayContent.Add, true); loadComponentSingleFile(skinEditor = new SkinEditorOverlay(screenContainer), overlayContent.Add); loadComponentSingleFile(new LoginOverlay @@ -769,7 +772,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; foreach (var overlay in singleDisplayOverlays) { diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index d049c2d3ec..3d88171ba7 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -92,6 +92,7 @@ namespace osu.Game.Overlays.Toolbar new ToolbarBeatmapListingButton(), new ToolbarChatButton(), new ToolbarSocialButton(), + new ToolbarWikiButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 961bd1177c9bfbeb3203aab22a4602fee3d3f6b2 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 00:39:36 +0200 Subject: [PATCH 010/165] Add mod "Random" for ruleset "osu!" --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 77 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game/Rulesets/Mods/ModRandomOsu.cs | 47 +++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs create mode 100644 osu.Game/Rulesets/Mods/ModRandomOsu.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs new file mode 100644 index 0000000000..f3c9040b1c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -0,0 +1,77 @@ +// 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.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModRandom : ModRandomOsu + { + protected override void RandomiseHitObjectPositions(IBeatmap beatmap) + { + var rng = new Random(); + + foreach (var hitObject in beatmap.HitObjects) + { + if (RandomiseCirclePositions.Value && hitObject is HitCircle circle) + { + circle.Position = new Vector2( + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + ); + } + else if (RandomiseSpinnerPositions.Value && hitObject is Spinner spinner) + { + spinner.Position = new Vector2( + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, + (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + ); + } + else if (RandomiseSliderPositions.Value && hitObject is Slider slider) + { + // Min. distances from the slider's position to the border to prevent the slider from being partially out of the screen + float minLeft = 0, minRight = 0, minTop = 0, minBottom = 0; + + var controlPointPositions = (from position + in slider.Path.ControlPoints + select position.Position.Value).ToList(); + + controlPointPositions.Add(slider.EndPosition); + controlPointPositions.RemoveAt(controlPointPositions.Count - 1); + + foreach (var position in controlPointPositions) + { + if (position.X > minRight) + { + minRight = position.X; + } + else if (-position.X > minLeft) + { + minLeft = -position.X; + } + + if (position.Y > minBottom) + { + minBottom = position.Y; + } + else if (-position.Y > minTop) + { + minTop = -position.Y; + } + } + + slider.Position = new Vector2( + (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.X - minLeft - minRight) + minLeft, + (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.Y - minTop - minBottom) + minTop + ); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 465d6d7155..6a04c4ca5c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -186,6 +186,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), new OsuModBarrelRoll(), + new OsuModRandom(), }; case ModType.System: diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs new file mode 100644 index 0000000000..9fb2c07d82 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModRandomOsu : Mod, IApplicableToBeatmap + { + public override string Name => "Random"; + public override string Acronym => "RD"; + public override IconUsage? Icon => OsuIcon.Dice; + public override ModType Type => ModType.Fun; + public override string Description => "Hit objects appear at random positions"; + public override double ScoreMultiplier => 1; + public override bool Ranked => false; + + [SettingSource("Randomise circle positions", "Hit circles appear at random positions")] + public Bindable RandomiseCirclePositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Randomise slider positions", "Sliders appear at random positions")] + public Bindable RandomiseSliderPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Randomise spinner positions", "Spinners appear at random positions")] + public Bindable RandomiseSpinnerPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + public void ApplyToBeatmap(IBeatmap beatmap) => RandomiseHitObjectPositions(beatmap); + + protected abstract void RandomiseHitObjectPositions(IBeatmap beatmap); + } +} From 817bb5213c48d0b4cf5f94a375c74e04c1a16ff5 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 00:46:35 +0200 Subject: [PATCH 011/165] Make OsuAutoGenerator spin the cursor around the position of the spinner instead of a set value This is to make Autoplay work with randomised spinner positions --- .../Replays/OsuAutoGenerator.cs | 18 +++++++++--------- .../Replays/OsuAutoGeneratorBase.cs | 3 --- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 7b0cf651c8..609799dc54 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -155,9 +155,9 @@ namespace osu.Game.Rulesets.Osu.Replays if (spinner.SpinsRequired == 0) return; - calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(spinner, ((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position; + Vector2 spinCentreOffset = spinner.Position - ((OsuReplayFrame)Frames[^1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -180,9 +180,9 @@ namespace osu.Game.Rulesets.Osu.Replays #region Helper subroutines - private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) + private static void calcSpinnerStartPosAndDirection(Spinner spinner, Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) { - Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; + Vector2 spinCentreOffset = spinner.Position - prevPos; float distFromCentre = spinCentreOffset.Length; float distToTangentPoint = MathF.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); @@ -216,13 +216,13 @@ namespace osu.Game.Rulesets.Osu.Replays else if (spinCentreOffset.Length > 0) { // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. - startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); + startPosition = spinner.Position - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); spinnerDirection = 1; } else { // Degenerate case where cursor position is exactly at the centre of the spin circle. - startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); + startPosition = spinner.Position + new Vector2(0, -SPIN_RADIUS); spinnerDirection = 1; } } @@ -335,7 +335,7 @@ namespace osu.Game.Rulesets.Osu.Replays { // We add intermediate frames for spinning / following a slider here. case Spinner spinner: - Vector2 difference = startPosition - SPINNER_CENTRE; + Vector2 difference = startPosition - spinner.Position; float radius = difference.Length; float angle = radius == 0 ? 0 : MathF.Atan2(difference.Y, difference.X); @@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection; angle += (float)t / 20; - Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); + Vector2 pos = spinner.Position + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action)); previousFrame = nextFrame; @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection; angle += (float)t / 20; - Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); + Vector2 endPosition = spinner.Position + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 1cb3208c30..69eb669a8e 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu.Replays @@ -20,8 +19,6 @@ namespace osu.Game.Rulesets.Osu.Replays /// /// Constants (for spinners). /// - protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2; - public const float SPIN_RADIUS = 50; #endregion From 8a3fa53c2661662272a62699eed7d7a1f04407ea Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:02:03 +0200 Subject: [PATCH 012/165] Change mod description and settings labels --- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs index 9fb2c07d82..6b86da357a 100644 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -15,25 +15,25 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "RD"; public override IconUsage? Icon => OsuIcon.Dice; public override ModType Type => ModType.Fun; - public override string Description => "Hit objects appear at random positions"; + public override string Description => "Practice your reaction time!"; public override double ScoreMultiplier => 1; public override bool Ranked => false; - [SettingSource("Randomise circle positions", "Hit circles appear at random positions")] + [SettingSource("Circles", "Hit circles appear at random positions")] public Bindable RandomiseCirclePositions { get; } = new BindableBool { Default = true, Value = true, }; - [SettingSource("Randomise slider positions", "Sliders appear at random positions")] + [SettingSource("Sliders", "Sliders appear at random positions")] public Bindable RandomiseSliderPositions { get; } = new BindableBool { Default = true, Value = true, }; - [SettingSource("Randomise spinner positions", "Spinners appear at random positions")] + [SettingSource("Spinners", "Spinners appear at random positions")] public Bindable RandomiseSpinnerPositions { get; } = new BindableBool { Default = true, From 92f765b9588e6de084b4d57d7e00a75a210c07ec Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:19:06 +0200 Subject: [PATCH 013/165] Change ModType from Fun to Conversion --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6a04c4ca5c..b50d3ad2b4 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu { new OsuModTarget(), new OsuModDifficultyAdjust(), - new OsuModClassic() + new OsuModClassic(), + new OsuModRandom(), }; case ModType.Automation: @@ -186,7 +187,6 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), new OsuModBarrelRoll(), - new OsuModRandom(), }; case ModType.System: diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs index 6b86da357a..1581065c01 100644 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ b/osu.Game/Rulesets/Mods/ModRandomOsu.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Random"; public override string Acronym => "RD"; public override IconUsage? Icon => OsuIcon.Dice; - public override ModType Type => ModType.Fun; + public override ModType Type => ModType.Conversion; public override string Description => "Practice your reaction time!"; public override double ScoreMultiplier => 1; public override bool Ranked => false; From f33f1b2bed13faebb9ad8d041077f172178e60fc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:34:39 +0200 Subject: [PATCH 014/165] Remove class "ModRandomOsu" and adjust code Add documentation comment for OsuModRandom --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 34 +++++++++++++++- osu.Game/Rulesets/Mods/ModRandomOsu.cs | 47 ---------------------- 2 files changed, 32 insertions(+), 49 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ModRandomOsu.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index f3c9040b1c..c87628b0e7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -3,17 +3,47 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModRandom : ModRandomOsu + /// + /// Mod that randomises the positions of the s + /// + public class OsuModRandom : ModRandom, IApplicableToBeatmap { - protected override void RandomiseHitObjectPositions(IBeatmap beatmap) + public override string Description => "Practice your reaction time!"; + public override bool Ranked => false; + + [SettingSource("Circles", "Hit circles appear at random positions")] + public Bindable RandomiseCirclePositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Sliders", "Sliders appear at random positions")] + public Bindable RandomiseSliderPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + [SettingSource("Spinners", "Spinners appear at random positions")] + public Bindable RandomiseSpinnerPositions { get; } = new BindableBool + { + Default = true, + Value = true, + }; + + public void ApplyToBeatmap(IBeatmap beatmap) { var rng = new Random(); diff --git a/osu.Game/Rulesets/Mods/ModRandomOsu.cs b/osu.Game/Rulesets/Mods/ModRandomOsu.cs deleted file mode 100644 index 1581065c01..0000000000 --- a/osu.Game/Rulesets/Mods/ModRandomOsu.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModRandomOsu : Mod, IApplicableToBeatmap - { - public override string Name => "Random"; - public override string Acronym => "RD"; - public override IconUsage? Icon => OsuIcon.Dice; - public override ModType Type => ModType.Conversion; - public override string Description => "Practice your reaction time!"; - public override double ScoreMultiplier => 1; - public override bool Ranked => false; - - [SettingSource("Circles", "Hit circles appear at random positions")] - public Bindable RandomiseCirclePositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Sliders", "Sliders appear at random positions")] - public Bindable RandomiseSliderPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Spinners", "Spinners appear at random positions")] - public Bindable RandomiseSpinnerPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - public void ApplyToBeatmap(IBeatmap beatmap) => RandomiseHitObjectPositions(beatmap); - - protected abstract void RandomiseHitObjectPositions(IBeatmap beatmap); - } -} From 08821da954d3927d776534fa1550d23838b3855a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 01:43:32 +0200 Subject: [PATCH 015/165] Change mod description --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index c87628b0e7..40e966a686 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// public class OsuModRandom : ModRandom, IApplicableToBeatmap { - public override string Description => "Practice your reaction time!"; + public override string Description => "It never gets boring!"; public override bool Ranked => false; [SettingSource("Circles", "Hit circles appear at random positions")] From 6e85c4e0699e85df8ebf841404cfd9e1dd652bae Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sun, 25 Apr 2021 23:57:01 +0200 Subject: [PATCH 016/165] Change randomisation process to keep distances between objects Remove now unnecessary settings; Remove spinners; Sliders are not implemented yet --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 254 +++++++++++++++------ 1 file changed, 183 insertions(+), 71 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 40e966a686..1d430ba711 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,10 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Bindables; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -22,86 +19,201 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; - [SettingSource("Circles", "Hit circles appear at random positions")] - public Bindable RandomiseCirclePositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Sliders", "Sliders appear at random positions")] - public Bindable RandomiseSliderPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; - - [SettingSource("Spinners", "Spinners appear at random positions")] - public Bindable RandomiseSpinnerPositions { get; } = new BindableBool - { - Default = true, - Value = true, - }; + // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The closer the hit objects draw to the border, the sharper the turn + private const byte border_distance_x = 128; + private const byte border_distance_y = 96; public void ApplyToBeatmap(IBeatmap beatmap) { var rng = new Random(); - foreach (var hitObject in beatmap.HitObjects) + // Absolute angle + float prevAngleRad = 0; + + // Absolute positions + Vector2 prevPosUnchanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + Vector2 prevPosChanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + + // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams + byte i = 5; + float rateOfChangeMultiplier = 0; + + foreach (var beatmapHitObject in beatmap.HitObjects) { - if (RandomiseCirclePositions.Value && hitObject is HitCircle circle) + if (!(beatmapHitObject is OsuHitObject hitObject)) + return; + + // posUnchanged: position from the original beatmap (not randomised) + var posUnchanged = hitObject.EndPosition; + var posChanged = Vector2.Zero; + + // Angle of the vector pointing from the last to the current hit object + float angleRad = 0; + + if (i >= 5) { - circle.Position = new Vector2( - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y + i = 0; + rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; + } + + if (hitObject is HitCircle circle) + { + var distanceToPrev = Vector2.Distance(posUnchanged, prevPosUnchanged); + + circle.Position = posChanged = getRandomisedPosition( + rateOfChangeMultiplier, + prevPosChanged, + prevAngleRad, + distanceToPrev, + out angleRad ); } - else if (RandomiseSpinnerPositions.Value && hitObject is Spinner spinner) - { - spinner.Position = new Vector2( - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.X, - (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y - ); - } - else if (RandomiseSliderPositions.Value && hitObject is Slider slider) - { - // Min. distances from the slider's position to the border to prevent the slider from being partially out of the screen - float minLeft = 0, minRight = 0, minTop = 0, minBottom = 0; - var controlPointPositions = (from position - in slider.Path.ControlPoints - select position.Position.Value).ToList(); + // TODO: Implement slider position randomisation - controlPointPositions.Add(slider.EndPosition); - controlPointPositions.RemoveAt(controlPointPositions.Count - 1); - - foreach (var position in controlPointPositions) - { - if (position.X > minRight) - { - minRight = position.X; - } - else if (-position.X > minLeft) - { - minLeft = -position.X; - } - - if (position.Y > minBottom) - { - minBottom = position.Y; - } - else if (-position.Y > minTop) - { - minTop = -position.Y; - } - } - - slider.Position = new Vector2( - (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.X - minLeft - minRight) + minLeft, - (float)rng.NextDouble() * (OsuPlayfield.BASE_SIZE.Y - minTop - minBottom) + minTop - ); - } + prevAngleRad = angleRad; + prevPosUnchanged = posUnchanged; + prevPosChanged = posChanged; + i++; } } + + /// + /// Returns the final position of the hit object + /// + /// Final position of the hit object + private Vector2 getRandomisedPosition( + float rateOfChangeMultiplier, + Vector2 prevPosChanged, + float prevAngleRad, + float distanceToPrev, + out float newAngle) + { + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) + // is proportional to the distance between the last and the current hit object + // to allow jumps and prevent too sharp turns during streams. + var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; + var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; + + newAngle = (float)randomAngleRad + prevAngleRad; + if (newAngle < 0) + newAngle += 2 * (float)Math.PI; + + var posRelativeToPrev = new Vector2( + distanceToPrev * (float)Math.Cos(newAngle), + distanceToPrev * (float)Math.Sin(newAngle) + ); + + posRelativeToPrev = getRotatedVector(prevPosChanged, posRelativeToPrev); + + newAngle = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevPosChanged, posRelativeToPrev); + + // Move hit objects back into the playfield if they are outside of it, + // which would sometimes happen during big jumps otherwise. + if (position.X < 0) + position.X = 0; + else if (position.X > OsuPlayfield.BASE_SIZE.X) + position.X = OsuPlayfield.BASE_SIZE.X; + + if (position.Y < 0) + position.Y = 0; + else if (position.Y > OsuPlayfield.BASE_SIZE.Y) + position.Y = OsuPlayfield.BASE_SIZE.Y; + + return position; + } + + /// + /// Determines the position of the current hit object relative to the previous one. + /// + /// The position of the current hit object relative to the previous one + private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) + { + var relativeRotationDistance = 0f; + var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + if (prevPosChanged.X < playfieldMiddle.X) + { + relativeRotationDistance = Math.Max( + (border_distance_x - prevPosChanged.X) / border_distance_x, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x, + relativeRotationDistance + ); + } + + if (prevPosChanged.Y < playfieldMiddle.Y) + { + relativeRotationDistance = Math.Max( + (border_distance_y - prevPosChanged.Y) / border_distance_y, + relativeRotationDistance + ); + } + else + { + relativeRotationDistance = Math.Max( + (prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y, + relativeRotationDistance + ); + } + + return rotateVectorTowardsVector( + posRelativeToPrev, + Vector2.Subtract(playfieldMiddle, prevPosChanged), + relativeRotationDistance + ); + } + + /// + /// Rotates vector "initial" towards vector "destinantion" + /// + /// Vector to rotate to "destination" + /// Vector "initial" should be rotated to + /// The angle the vector should be rotated relative to the difference between the angles of the the two vectors. + /// Resulting vector + private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance) + { + var initialAngleRad = Math.Atan2(initial.Y, initial.X); + var destAngleRad = Math.Atan2(destination.Y, destination.X); + + // Divide by 2 to limit the max. angle to 90° + // (90° is enough to prevent the hit objects from leaving the playfield) + relativeDistance /= 2; + + var diff = destAngleRad - initialAngleRad; + + while (diff < -Math.PI) + { + diff += 2 * Math.PI; + } + + while (diff > Math.PI) + { + diff -= 2 * Math.PI; + } + + var finalAngle = 0d; + + if (diff > 0) + { + finalAngle = initialAngleRad + relativeDistance * diff; + } + else if (diff < 0) + { + finalAngle = initialAngleRad + relativeDistance * diff; + } + + return new Vector2( + initial.Length * (float)Math.Cos(finalAngle), + initial.Length * (float)Math.Sin(finalAngle) + ); + } } } From 19fc2243489235598d523104296ec51448d6d897 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 26 Apr 2021 11:52:46 +0200 Subject: [PATCH 017/165] Revert "Make OsuAutoGenerator spin the cursor around the position of the spinner instead of a set value" This reverts commit 817bb5213c48d0b4cf5f94a375c74e04c1a16ff5. --- .../Replays/OsuAutoGenerator.cs | 18 +++++++++--------- .../Replays/OsuAutoGeneratorBase.cs | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 609799dc54..7b0cf651c8 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -155,9 +155,9 @@ namespace osu.Game.Rulesets.Osu.Replays if (spinner.SpinsRequired == 0) return; - calcSpinnerStartPosAndDirection(spinner, ((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = spinner.Position - ((OsuReplayFrame)Frames[^1]).Position; + Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -180,9 +180,9 @@ namespace osu.Game.Rulesets.Osu.Replays #region Helper subroutines - private static void calcSpinnerStartPosAndDirection(Spinner spinner, Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) + private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection) { - Vector2 spinCentreOffset = spinner.Position - prevPos; + Vector2 spinCentreOffset = SPINNER_CENTRE - prevPos; float distFromCentre = spinCentreOffset.Length; float distToTangentPoint = MathF.Sqrt(distFromCentre * distFromCentre - SPIN_RADIUS * SPIN_RADIUS); @@ -216,13 +216,13 @@ namespace osu.Game.Rulesets.Osu.Replays else if (spinCentreOffset.Length > 0) { // Previous cursor position was inside spin circle, set startPosition to the nearest point on spin circle. - startPosition = spinner.Position - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); + startPosition = SPINNER_CENTRE - spinCentreOffset * (SPIN_RADIUS / spinCentreOffset.Length); spinnerDirection = 1; } else { // Degenerate case where cursor position is exactly at the centre of the spin circle. - startPosition = spinner.Position + new Vector2(0, -SPIN_RADIUS); + startPosition = SPINNER_CENTRE + new Vector2(0, -SPIN_RADIUS); spinnerDirection = 1; } } @@ -335,7 +335,7 @@ namespace osu.Game.Rulesets.Osu.Replays { // We add intermediate frames for spinning / following a slider here. case Spinner spinner: - Vector2 difference = startPosition - spinner.Position; + Vector2 difference = startPosition - SPINNER_CENTRE; float radius = difference.Length; float angle = radius == 0 ? 0 : MathF.Atan2(difference.Y, difference.X); @@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection; angle += (float)t / 20; - Vector2 pos = spinner.Position + CirclePosition(angle, SPIN_RADIUS); + Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action)); previousFrame = nextFrame; @@ -357,7 +357,7 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection; angle += (float)t / 20; - Vector2 endPosition = spinner.Position + CirclePosition(angle, SPIN_RADIUS); + Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame(spinner.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 69eb669a8e..1cb3208c30 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Replays; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu.Replays @@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Replays /// /// Constants (for spinners). /// + protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2; + public const float SPIN_RADIUS = 50; #endregion From 1dfe028c0209d4ef4cac6c3c2462af22797d81a9 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 26 Apr 2021 22:26:13 +0200 Subject: [PATCH 018/165] Fix bug causing the star rating to change when Random is enabled --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ++++++++++ .../Mods/IApplicableToBeatmapKeepStarRating.cs | 14 ++++++++++++++ osu.Game/Screens/Play/Player.cs | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1d430ba711..3a4b5073b5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// - public class OsuModRandom : ModRandom, IApplicableToBeatmap + public class OsuModRandom : ModRandom, IApplicableToBeatmapKeepStarRating { public override string Description => "It never gets boring!"; public override bool Ranked => false; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index e0eeaf6db0..d49f6ed50b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -32,6 +32,11 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; + /// + /// Only if this is set to true, changes made by mods that implement will be applied. + /// + public bool ApplyChangesToBeatmap; + protected AudioManager AudioManager { get; } protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) @@ -166,10 +171,15 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { + if (mod is IApplicableToBeatmapKeepStarRating && !ApplyChangesToBeatmap) + continue; + cancellationSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } + ApplyChangesToBeatmap = false; + return converted; } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs new file mode 100644 index 0000000000..34db7e1be3 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Interface for a that applies changes to a after conversion and post-processing has completed without changing its difficulty + /// + public interface IApplicableToBeatmapKeepStarRating : IApplicableToBeatmap + { + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 27a4fcc291..80eec64884 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -455,6 +455,8 @@ namespace osu.Game.Screens.Play rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); + Beatmap.Value.ApplyChangesToBeatmap = true; + try { playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); From 4b05568d2d0159e98eb0b7664e87bad48fe8200a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 19:39:58 +0200 Subject: [PATCH 019/165] Revert "Fix bug causing the star rating to change when Random is enabled" This reverts commit 1dfe028c0209d4ef4cac6c3c2462af22797d81a9. --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ---------- .../Mods/IApplicableToBeatmapKeepStarRating.cs | 14 -------------- osu.Game/Screens/Play/Player.cs | 2 -- 4 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 3a4b5073b5..1d430ba711 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// - public class OsuModRandom : ModRandom, IApplicableToBeatmapKeepStarRating + public class OsuModRandom : ModRandom, IApplicableToBeatmap { public override string Description => "It never gets boring!"; public override bool Ranked => false; diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d49f6ed50b..e0eeaf6db0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -32,11 +32,6 @@ namespace osu.Game.Beatmaps public readonly BeatmapMetadata Metadata; - /// - /// Only if this is set to true, changes made by mods that implement will be applied. - /// - public bool ApplyChangesToBeatmap; - protected AudioManager AudioManager { get; } protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) @@ -171,15 +166,10 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { - if (mod is IApplicableToBeatmapKeepStarRating && !ApplyChangesToBeatmap) - continue; - cancellationSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } - ApplyChangesToBeatmap = false; - return converted; } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs deleted file mode 100644 index 34db7e1be3..0000000000 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapKeepStarRating.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// Interface for a that applies changes to a after conversion and post-processing has completed without changing its difficulty - /// - public interface IApplicableToBeatmapKeepStarRating : IApplicableToBeatmap - { - } -} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 80eec64884..27a4fcc291 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -455,8 +455,6 @@ namespace osu.Game.Screens.Play rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); - Beatmap.Value.ApplyChangesToBeatmap = true; - try { playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); From a141a4e9e6641e322740240258179d5b06718b0b Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 20:44:36 +0200 Subject: [PATCH 020/165] Add setting "Seed" Random numbers are now generated with the seed specified in the mod settings. --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1d430ba711..675aa4a0b3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,7 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; +using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -24,9 +27,23 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 128; private const byte border_distance_y = 96; + [SettingSource("Seed", "Seed for the random number generator")] + public Bindable Seed { get; } = new Bindable + { + Value = "0" + }; + public void ApplyToBeatmap(IBeatmap beatmap) { - var rng = new Random(); + if (!int.TryParse(Seed.Value, out var seed)) + { + var e = new FormatException("Seed must be an integer"); + Logger.Error(e, "Could not load beatmap: RNG seed must be an integer."); + + return; + } + + var rng = new Random(seed); // Absolute angle float prevAngleRad = 0; From 95040f7edc42262fd43cfddac8a4315bc0c402a6 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 27 Apr 2021 22:19:04 +0200 Subject: [PATCH 021/165] Change initial seed to a random number --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 675aa4a0b3..0124a3c28e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods [SettingSource("Seed", "Seed for the random number generator")] public Bindable Seed { get; } = new Bindable { - Value = "0" + Value = RNG.Next().ToString() }; public void ApplyToBeatmap(IBeatmap beatmap) From 6bed268bd8ff7719b36bdba5e253de0291c6deaa Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 1 May 2021 04:01:43 +0200 Subject: [PATCH 022/165] Enhance mod settings and add option "Random seed" + slight adjustments --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 161 ++++++++++++++++++--- 1 file changed, 144 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 0124a3c28e..9a6127cdad 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,13 +2,20 @@ // 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.Logging; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osuTK; @@ -25,36 +32,58 @@ namespace osu.Game.Rulesets.Osu.Mods // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 128; - private const byte border_distance_y = 96; + private const byte border_distance_x = 192; + private const byte border_distance_y = 144; - [SettingSource("Seed", "Seed for the random number generator")] - public Bindable Seed { get; } = new Bindable + private static readonly Bindable seed = new Bindable { - Value = RNG.Next().ToString() + Default = -1 }; - public void ApplyToBeatmap(IBeatmap beatmap) + private static readonly BindableBool random_seed = new BindableBool { - if (!int.TryParse(Seed.Value, out var seed)) - { - var e = new FormatException("Seed must be an integer"); - Logger.Error(e, "Could not load beatmap: RNG seed must be an integer."); + Value = true, + Default = true + }; + [SettingSource("Random seed", "Generate a random seed for the beatmap generation")] + public BindableBool RandomSeed => random_seed; + + [SettingSource("Seed", "Seed for the random beatmap generation", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable Seed => seed; + + internal static bool CustomSeedDisabled => random_seed.Value; + + public OsuModRandom() + { + if (seed.Default != -1) return; - } - var rng = new Random(seed); + var random = RNG.Next(); + seed.Value = random; + seed.Default = random; + seed.BindValueChanged(e => seed.Default = e.NewValue); + } + + public void ApplyToBeatmap(IBeatmap iBeatmap) + { + if (!(iBeatmap is OsuBeatmap beatmap)) + return; + + if (RandomSeed.Value) + seed.Value = RNG.Next(); + + var rng = new Random(seed.Value); // Absolute angle float prevAngleRad = 0; // Absolute positions - Vector2 prevPosUnchanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; - Vector2 prevPosChanged = ((OsuHitObject)beatmap.HitObjects[0]).Position; + Vector2 prevPosUnchanged = beatmap.HitObjects[0].Position; + Vector2 prevPosChanged = beatmap.HitObjects[0].Position; // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams - byte i = 5; + byte i = 3; float rateOfChangeMultiplier = 0; foreach (var beatmapHitObject in beatmap.HitObjects) @@ -69,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Angle of the vector pointing from the last to the current hit object float angleRad = 0; - if (i >= 5) + if (i >= 3) { i = 0; rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; @@ -234,4 +263,102 @@ namespace osu.Game.Rulesets.Osu.Mods ); } } + + public class OsuModRandomSettingsControl : SettingsItem + { + [Resolved] + private static GameHost host { get; set; } + + [BackgroundDependencyLoader] + private void load(GameHost gameHost) => host = gameHost; + + protected override Drawable CreateControl() => new SeedControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; + + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current; + set => Scheduler.Add(() => current.Current = value); + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + }, + null, + new TriangleButton + { + RelativeSizeAxes = Axes.Both, + Height = 1, + Text = "Copy", + Action = copySeedToClipboard + } + } + } + } + }; + + seedNumberBox.Current.BindValueChanged(onTextBoxValueChanged); + } + + private void onTextBoxValueChanged(ValueChangedEvent e) + { + string seed = e.NewValue; + + while (!string.IsNullOrEmpty(seed) && !int.TryParse(seed, out _)) + seed = seed[..^1]; + + if (!int.TryParse(seed, out var intVal)) + intVal = 0; + + current.Value = intVal; + } + + private void copySeedToClipboard() => host.GetClipboard().SetText(seedNumberBox.Text); + + protected override void Update() + { + seedNumberBox.ReadOnly = OsuModRandom.CustomSeedDisabled; + + if (seedNumberBox.HasFocus) + return; + + seedNumberBox.Text = current.Current.Value.ToString(); + } + } + } } From 946abfbb83d89caea8e7273901a1e1a020824813 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Wed, 12 May 2021 18:11:50 +0200 Subject: [PATCH 023/165] Rework settings; Add seed to ScorePanel; Apply requested changes from @bdach --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 200 +++++++----------- osu.Game/Rulesets/Mods/ModRandom.cs | 1 + osu.Game/Scoring/ScoreInfo.cs | 18 ++ .../Expanded/ExpandedPanelMiddleContent.cs | 47 +++- osu.Game/Screens/Ranking/ScorePanel.cs | 8 +- 5 files changed, 152 insertions(+), 122 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 9a6127cdad..2d7c52d535 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -35,68 +35,46 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; - private static readonly Bindable seed = new Bindable + [SettingSource("Custom seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable CustomSeed { get; } = new Bindable { - Default = -1 + Default = null, + Value = null }; - private static readonly BindableBool random_seed = new BindableBool + public void ApplyToBeatmap(IBeatmap beatmap) { - Value = true, - Default = true - }; - - [SettingSource("Random seed", "Generate a random seed for the beatmap generation")] - public BindableBool RandomSeed => random_seed; - - [SettingSource("Seed", "Seed for the random beatmap generation", SettingControlType = typeof(OsuModRandomSettingsControl))] - public Bindable Seed => seed; - - internal static bool CustomSeedDisabled => random_seed.Value; - - public OsuModRandom() - { - if (seed.Default != -1) + if (!(beatmap is OsuBeatmap osuBeatmap)) return; - var random = RNG.Next(); - seed.Value = random; - seed.Default = random; - seed.BindValueChanged(e => seed.Default = e.NewValue); - } + var seed = RNG.Next(); - public void ApplyToBeatmap(IBeatmap iBeatmap) - { - if (!(iBeatmap is OsuBeatmap beatmap)) - return; + if (CustomSeed.Value != null) + seed = (int)CustomSeed.Value; - if (RandomSeed.Value) - seed.Value = RNG.Next(); + Seed = seed; - var rng = new Random(seed.Value); + var rng = new Random(seed); - // Absolute angle - float prevAngleRad = 0; - - // Absolute positions - Vector2 prevPosUnchanged = beatmap.HitObjects[0].Position; - Vector2 prevPosChanged = beatmap.HitObjects[0].Position; + var prevObjectInfo = new HitObjectInfo + { + AngleRad = 0, + PosUnchanged = osuBeatmap.HitObjects[0].Position, + PosChanged = osuBeatmap.HitObjects[0].Position + }; // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams byte i = 3; float rateOfChangeMultiplier = 0; - foreach (var beatmapHitObject in beatmap.HitObjects) + foreach (var currentHitObject in osuBeatmap.HitObjects) { - if (!(beatmapHitObject is OsuHitObject hitObject)) - return; - - // posUnchanged: position from the original beatmap (not randomised) - var posUnchanged = hitObject.EndPosition; - var posChanged = Vector2.Zero; - - // Angle of the vector pointing from the last to the current hit object - float angleRad = 0; + var currentObjectInfo = new HitObjectInfo + { + AngleRad = 0, + PosUnchanged = currentHitObject.EndPosition, + PosChanged = Vector2.Zero + }; if (i >= 3) { @@ -104,24 +82,23 @@ namespace osu.Game.Rulesets.Osu.Mods rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; } - if (hitObject is HitCircle circle) + if (currentHitObject is HitCircle circle) { - var distanceToPrev = Vector2.Distance(posUnchanged, prevPosUnchanged); + var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); - circle.Position = posChanged = getRandomisedPosition( + getObjectInfo( rateOfChangeMultiplier, - prevPosChanged, - prevAngleRad, + prevObjectInfo, distanceToPrev, - out angleRad + ref currentObjectInfo ); + + circle.Position = currentObjectInfo.PosChanged; } // TODO: Implement slider position randomisation - prevAngleRad = angleRad; - prevPosUnchanged = posUnchanged; - prevPosChanged = posChanged; + prevObjectInfo = currentObjectInfo; i++; } } @@ -130,12 +107,11 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private Vector2 getRandomisedPosition( + private void getObjectInfo( float rateOfChangeMultiplier, - Vector2 prevPosChanged, - float prevAngleRad, + HitObjectInfo prevObjectInfo, float distanceToPrev, - out float newAngle) + ref HitObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -143,19 +119,19 @@ namespace osu.Game.Rulesets.Osu.Mods var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; - newAngle = (float)randomAngleRad + prevAngleRad; - if (newAngle < 0) - newAngle += 2 * (float)Math.PI; + currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; + if (currentObjectInfo.AngleRad < 0) + currentObjectInfo.AngleRad += 2 * (float)Math.PI; var posRelativeToPrev = new Vector2( - distanceToPrev * (float)Math.Cos(newAngle), - distanceToPrev * (float)Math.Sin(newAngle) + distanceToPrev * (float)Math.Cos(currentObjectInfo.AngleRad), + distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevPosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.PosChanged, posRelativeToPrev); - newAngle = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevPosChanged, posRelativeToPrev); + currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevObjectInfo.PosChanged, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -169,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - return position; + currentObjectInfo.PosChanged = position; } /// @@ -214,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Mods return rotateVectorTowardsVector( posRelativeToPrev, Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance + relativeRotationDistance / 2 ); } @@ -230,10 +206,6 @@ namespace osu.Game.Rulesets.Osu.Mods var initialAngleRad = Math.Atan2(initial.Y, initial.X); var destAngleRad = Math.Atan2(destination.Y, destination.X); - // Divide by 2 to limit the max. angle to 90° - // (90° is enough to prevent the hit objects from leaving the playfield) - relativeDistance /= 2; - var diff = destAngleRad - initialAngleRad; while (diff < -Math.PI) @@ -246,46 +218,45 @@ namespace osu.Game.Rulesets.Osu.Mods diff -= 2 * Math.PI; } - var finalAngle = 0d; - - if (diff > 0) - { - finalAngle = initialAngleRad + relativeDistance * diff; - } - else if (diff < 0) - { - finalAngle = initialAngleRad + relativeDistance * diff; - } + var finalAngleRad = initialAngleRad + relativeDistance * diff; return new Vector2( - initial.Length * (float)Math.Cos(finalAngle), - initial.Length * (float)Math.Sin(finalAngle) + initial.Length * (float)Math.Cos(finalAngleRad), + initial.Length * (float)Math.Sin(finalAngleRad) ); } + + private struct HitObjectInfo + { + internal float AngleRad { get; set; } + internal Vector2 PosUnchanged { get; set; } + internal Vector2 PosChanged { get; set; } + } } - public class OsuModRandomSettingsControl : SettingsItem + public class OsuModRandomSettingsControl : SettingsItem { - [Resolved] - private static GameHost host { get; set; } - - [BackgroundDependencyLoader] - private void load(GameHost gameHost) => host = gameHost; - protected override Drawable CreateControl() => new SeedControl { RelativeSizeAxes = Axes.X, Margin = new MarginPadding { Top = 5 } }; - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + [Resolved] + private GameHost host { get; set; } - public Bindable Current + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current { get => current; - set => Scheduler.Add(() => current.Current = value); + set + { + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } } private readonly OsuNumberBox seedNumberBox; @@ -324,40 +295,29 @@ namespace osu.Game.Rulesets.Osu.Mods { RelativeSizeAxes = Axes.Both, Height = 1, - Text = "Copy", - Action = copySeedToClipboard + Text = "Paste", + Action = () => seedNumberBox.Text = host.GetClipboard().GetText() } } } } }; - seedNumberBox.Current.BindValueChanged(onTextBoxValueChanged); + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); } - private void onTextBoxValueChanged(ValueChangedEvent e) - { - string seed = e.NewValue; - - while (!string.IsNullOrEmpty(seed) && !int.TryParse(seed, out _)) - seed = seed[..^1]; - - if (!int.TryParse(seed, out var intVal)) - intVal = 0; - - current.Value = intVal; - } - - private void copySeedToClipboard() => host.GetClipboard().SetText(seedNumberBox.Text); - protected override void Update() { - seedNumberBox.ReadOnly = OsuModRandom.CustomSeedDisabled; - - if (seedNumberBox.HasFocus) - return; - - seedNumberBox.Text = current.Current.Value.ToString(); + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); } } } diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index da55ab3fbf..382792f75c 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,5 +13,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; + public int? Seed { get; protected set; } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a6faaf6379..b584d24370 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -88,6 +88,24 @@ namespace osu.Game.Scoring } } + public bool ContainsModOfType(out T mod) + { + if (mods != null) + { + foreach (var currentMod in mods) + { + if (!(currentMod is T modOfType)) + continue; + + mod = modOfType; + return true; + } + } + + mod = default; + return false; + } + // Used for API serialisation/deserialisation. [JsonProperty("mods")] [NotMapped] diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 6a6b39b61c..4d81290a75 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -7,11 +7,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -55,6 +57,9 @@ namespace osu.Game.Screens.Ranking.Expanded Padding = new MarginPadding(padding); } + [Resolved] + private GameHost host { get; set; } + [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache beatmapDifficultyCache) { @@ -224,7 +229,47 @@ namespace osu.Game.Screens.Ranking.Expanded } } } - } + }.With(t => + { + if (!score.ContainsModOfType(out var mod) || mod.Seed == null) + return; + + t.Add(new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] + { + new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding + { + Top = 3f, + Right = 5f + }, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Medium), + Text = $"Seed: {mod.Seed}" + }, + new TriangleButton + { + RelativeSizeAxes = Axes.Both, + Height = 1.2f, + Width = 0.5f, + Text = "Copy", + Action = () => host.GetClipboard().SetText(mod.Seed.ToString()) + } + } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + } + }); + }) } }, new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index df710e4eb8..33b06571fe 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; @@ -206,7 +207,12 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - Size = new Vector2(EXPANDED_WIDTH, expanded_height); + var height = expanded_height; + + if (Score.ContainsModOfType(out var mod) && mod.Seed != null) + height += 20f; + + Size = new Vector2(EXPANDED_WIDTH, height); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From a9d5211e81593f30be9f7d7c5a91218a52b47a2a Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 01:42:39 +0200 Subject: [PATCH 024/165] Remove seed from the ScorePanel and "Paste" button --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 15 ------ osu.Game/Rulesets/Mods/ModRandom.cs | 1 - osu.Game/Scoring/ScoreInfo.cs | 18 ------- .../Expanded/ExpandedPanelMiddleContent.cs | 47 +------------------ osu.Game/Screens/Ranking/ScorePanel.cs | 8 +--- 5 files changed, 2 insertions(+), 87 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 2d7c52d535..1b8f8ee7f5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,12 +2,10 @@ // 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.UserInterface; -using osu.Framework.Platform; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -52,8 +50,6 @@ namespace osu.Game.Rulesets.Osu.Mods if (CustomSeed.Value != null) seed = (int)CustomSeed.Value; - Seed = seed; - var rng = new Random(seed); var prevObjectInfo = new HitObjectInfo @@ -244,9 +240,6 @@ namespace osu.Game.Rulesets.Osu.Mods private sealed class SeedControl : CompositeDrawable, IHasCurrentValue { - [Resolved] - private GameHost host { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); public Bindable Current @@ -289,14 +282,6 @@ namespace osu.Game.Rulesets.Osu.Mods { RelativeSizeAxes = Axes.X, CommitOnFocusLost = true - }, - null, - new TriangleButton - { - RelativeSizeAxes = Axes.Both, - Height = 1, - Text = "Paste", - Action = () => seedNumberBox.Text = host.GetClipboard().GetText() } } } diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs index 382792f75c..da55ab3fbf 100644 --- a/osu.Game/Rulesets/Mods/ModRandom.cs +++ b/osu.Game/Rulesets/Mods/ModRandom.cs @@ -13,6 +13,5 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.Conversion; public override IconUsage? Icon => OsuIcon.Dice; public override double ScoreMultiplier => 1; - public int? Seed { get; protected set; } } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index b584d24370..a6faaf6379 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -88,24 +88,6 @@ namespace osu.Game.Scoring } } - public bool ContainsModOfType(out T mod) - { - if (mods != null) - { - foreach (var currentMod in mods) - { - if (!(currentMod is T modOfType)) - continue; - - mod = modOfType; - return true; - } - } - - mod = default; - return false; - } - // Used for API serialisation/deserialisation. [JsonProperty("mods")] [NotMapped] diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 4d81290a75..6a6b39b61c 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -7,13 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -57,9 +55,6 @@ namespace osu.Game.Screens.Ranking.Expanded Padding = new MarginPadding(padding); } - [Resolved] - private GameHost host { get; set; } - [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache beatmapDifficultyCache) { @@ -229,47 +224,7 @@ namespace osu.Game.Screens.Ranking.Expanded } } } - }.With(t => - { - if (!score.ContainsModOfType(out var mod) || mod.Seed == null) - return; - - t.Add(new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Content = new[] - { - new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding - { - Top = 3f, - Right = 5f - }, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Medium), - Text = $"Seed: {mod.Seed}" - }, - new TriangleButton - { - RelativeSizeAxes = Axes.Both, - Height = 1.2f, - Width = 0.5f, - Text = "Copy", - Action = () => host.GetClipboard().SetText(mod.Seed.ToString()) - } - } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - } - }); - }) + } } }, new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 33b06571fe..df710e4eb8 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking.Contracted; using osu.Game.Screens.Ranking.Expanded; @@ -207,12 +206,7 @@ namespace osu.Game.Screens.Ranking switch (state) { case PanelState.Expanded: - var height = expanded_height; - - if (Score.ContainsModOfType(out var mod) && mod.Seed != null) - height += 20f; - - Size = new Vector2(EXPANDED_WIDTH, height); + Size = new Vector2(EXPANDED_WIDTH, expanded_height); topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); From ac04e8afa28a8bc5041176f0c59f8c77a109d2f2 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 01:50:11 +0200 Subject: [PATCH 025/165] Change name of option "Custom seed" to "Seed" and set its value to the generated seed --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1b8f8ee7f5..1141de58a2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -33,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods private const byte border_distance_x = 192; private const byte border_distance_y = 144; - [SettingSource("Custom seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] - public Bindable CustomSeed { get; } = new Bindable + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] + public Bindable Seed { get; } = new Bindable { Default = null, Value = null @@ -45,12 +45,9 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(beatmap is OsuBeatmap osuBeatmap)) return; - var seed = RNG.Next(); + Seed.Value ??= RNG.Next(); - if (CustomSeed.Value != null) - seed = (int)CustomSeed.Value; - - var rng = new Random(seed); + var rng = new Random((int)Seed.Value); var prevObjectInfo = new HitObjectInfo { From dbc23187105279560e8a7bb26d206a450cf4604d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 14:13:35 +0900 Subject: [PATCH 026/165] Initial tidying up --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 29 +++++++++------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1141de58a2..dc194aa464 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -45,6 +45,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(beatmap is OsuBeatmap osuBeatmap)) return; + var hitObjects = osuBeatmap.HitObjects; + Seed.Value ??= RNG.Next(); var rng = new Random((int)Seed.Value); @@ -52,30 +54,28 @@ namespace osu.Game.Rulesets.Osu.Mods var prevObjectInfo = new HitObjectInfo { AngleRad = 0, - PosUnchanged = osuBeatmap.HitObjects[0].Position, - PosChanged = osuBeatmap.HitObjects[0].Position + PosUnchanged = hitObjects[0].Position, + PosChanged = hitObjects[0].Position }; - // rateOfChangeMultiplier changes every i iterations to prevent shaky-line-shaped streams - byte i = 3; float rateOfChangeMultiplier = 0; - foreach (var currentHitObject in osuBeatmap.HitObjects) + for (int i = 0; i < hitObjects.Count; i++) { + var h = hitObjects[i]; + var currentObjectInfo = new HitObjectInfo { AngleRad = 0, - PosUnchanged = currentHitObject.EndPosition, + PosUnchanged = h.EndPosition, PosChanged = Vector2.Zero }; - if (i >= 3) - { - i = 0; + // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams + if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - } - if (currentHitObject is HitCircle circle) + if (h is HitCircle circle) { var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); @@ -92,7 +92,6 @@ namespace osu.Game.Rulesets.Osu.Mods // TODO: Implement slider position randomisation prevObjectInfo = currentObjectInfo; - i++; } } @@ -100,11 +99,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void getObjectInfo( - float rateOfChangeMultiplier, - HitObjectInfo prevObjectInfo, - float distanceToPrev, - ref HitObjectInfo currentObjectInfo) + private void getObjectInfo(float rateOfChangeMultiplier, HitObjectInfo prevObjectInfo, float distanceToPrev, ref HitObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object From 3fa6a0413b8a3a3223432301e5f35057177e81dc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Fri, 14 May 2021 23:04:09 +0200 Subject: [PATCH 027/165] Add slider position randomisation --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 90 ++++++++++++++++------ 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index dc194aa464..90036e6839 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -53,9 +53,10 @@ namespace osu.Game.Rulesets.Osu.Mods var prevObjectInfo = new HitObjectInfo { - AngleRad = 0, - PosUnchanged = hitObjects[0].Position, - PosChanged = hitObjects[0].Position + StartPosUnchanged = hitObjects[0].Position, + EndPosUnchanged = hitObjects[0].EndPosition, + StartPosChanged = hitObjects[0].Position, + EndPosChanged = hitObjects[0].EndPosition }; float rateOfChangeMultiplier = 0; @@ -66,31 +67,51 @@ namespace osu.Game.Rulesets.Osu.Mods var currentObjectInfo = new HitObjectInfo { - AngleRad = 0, - PosUnchanged = h.EndPosition, - PosChanged = Vector2.Zero + StartPosUnchanged = h.Position, + EndPosUnchanged = h.EndPosition, + StartPosChanged = Vector2.Zero, + EndPosChanged = Vector2.Zero }; // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - if (h is HitCircle circle) + var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPosUnchanged, currentObjectInfo.StartPosUnchanged); + + switch (h) { - var distanceToPrev = Vector2.Distance(currentObjectInfo.PosUnchanged, prevObjectInfo.PosUnchanged); + case HitCircle circle: + getObjectInfo( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); - getObjectInfo( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); + circle.Position = currentObjectInfo.StartPosChanged; + currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + break; - circle.Position = currentObjectInfo.PosChanged; + case Slider slider: + currentObjectInfo.EndPosUnchanged = slider.EndPosition; + + currentObjectInfo.EndPosUnchanged = slider.TailCircle.Position; + + getObjectInfo( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); + + slider.Position = currentObjectInfo.StartPosChanged; + currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + + moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + break; } - // TODO: Implement slider position randomisation - prevObjectInfo = currentObjectInfo; } } @@ -116,10 +137,10 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.PosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPosChanged, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.PosChanged, posRelativeToPrev); + var position = Vector2.Add(prevObjectInfo.EndPosChanged, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -133,7 +154,30 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - currentObjectInfo.PosChanged = position; + currentObjectInfo.StartPosChanged = position; + } + + private void moveSliderIntoPlayfield(ref Slider slider, ref HitObjectInfo currentObjectInfo) + { + foreach (var controlPoint in slider.Path.ControlPoints) + { + // Position of controlPoint relative to slider.Position + var pos = controlPoint.Position.Value; + + var playfieldSize = OsuPlayfield.BASE_SIZE; + + if (pos.X + slider.Position.X < 0) + slider.Position = new Vector2(-pos.X, slider.Position.Y); + else if (pos.X + slider.Position.X > playfieldSize.X) + slider.Position = new Vector2(playfieldSize.X - pos.X, slider.Position.Y); + + if (pos.Y + slider.Position.Y < 0) + slider.Position = new Vector2(slider.Position.X, -pos.Y); + else if (pos.Y + slider.Position.Y > playfieldSize.Y) + slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); + } + + currentObjectInfo.EndPosChanged = slider.TailCircle.Position; } /// @@ -217,8 +261,10 @@ namespace osu.Game.Rulesets.Osu.Mods private struct HitObjectInfo { internal float AngleRad { get; set; } - internal Vector2 PosUnchanged { get; set; } - internal Vector2 PosChanged { get; set; } + internal Vector2 StartPosUnchanged { get; set; } + internal Vector2 EndPosUnchanged { get; set; } + internal Vector2 StartPosChanged { get; set; } + internal Vector2 EndPosChanged { get; set; } } } From 878182fbdf480348d1114f0bc1e9f47c6141552c Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Sat, 15 May 2021 02:07:24 +0200 Subject: [PATCH 028/165] Fix slider ticks not being shifted along with their parent sliders --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 90036e6839..d06e807500 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -2,6 +2,7 @@ // 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; using osu.Framework.Graphics.Containers; @@ -91,6 +92,7 @@ namespace osu.Game.Rulesets.Osu.Mods circle.Position = currentObjectInfo.StartPosChanged; currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + break; case Slider slider: @@ -109,6 +111,12 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.EndPosChanged = slider.TailCircle.Position; moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + + var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.StartPosUnchanged); + + foreach (var tick in slider.NestedHitObjects.OfType()) + tick.Position = Vector2.Add(tick.Position, sliderShift); + break; } From 4c25fe750f193f7e624ffce2185070d85267e73b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 14:32:15 +0300 Subject: [PATCH 029/165] Disallow beatmap skin to fall back to default HUD components This should become a more generalized `AllowDefaultSkinFallback` when default legacy skin fallback is supported. --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 1 + osu.Game/Skinning/LegacySkin.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..1a298576f9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -14,6 +14,7 @@ namespace osu.Game.Skinning public class LegacyBeatmapSkin : LegacySkin { protected override bool AllowManiaSkin => false; + protected override bool AllowDefaultHUDComponentsFallback => false; protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a6f8f45c0f..e7edba1e13 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,6 +38,11 @@ namespace osu.Game.Skinning protected virtual bool AllowManiaSkin => hasKeyTexture.Value; + /// + /// Whether this skin will fall back to default HUD components if it has no fonts for use. + /// + protected virtual bool AllowDefaultHUDComponentsFallback => true; + /// /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). /// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank). @@ -331,6 +336,8 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: + if (!this.HasFont(LegacyFont.Score) && !AllowDefaultHUDComponentsFallback) + return null; var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { From 102842bcf1c1919c75a7693771e6b3ce51d83e6f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 14:32:58 +0300 Subject: [PATCH 030/165] Expire legacy combo counters on catch ruleset --- .../Legacy/CatchLegacySkinTransformer.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..6c477154ac 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osuTK.Graphics; @@ -22,14 +24,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is HUDSkinComponent hudComponent) + if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; - } + if (!providesComboCounter || !(Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components)) + return null; + + // catch may provide its own combo counter; hide the default. + // todo: this should probably be done in an elegant way. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.Expire(); + + return components; } if (!(component is CatchSkinComponent catchSkinComponent)) From 243c8aa58557719f8ce8983dd848395e4f399d44 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 18:02:38 +0300 Subject: [PATCH 031/165] Add test coverage --- .../TestSceneCatchPlayerLegacySkin.cs | 26 +++++++++++++++++++ .../Tests/Visual/LegacySkinPlayerTestScene.cs | 10 +++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 64695153b5..d49470079b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,8 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Catch.Tests { @@ -10,5 +18,23 @@ namespace osu.Game.Rulesets.Catch.Tests public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + + [Test] + [Ignore("HUD components broken, remove when fixed.")] + public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) + { + if (withModifiedSkin) + { + AddStep("change component scale", () => Player.ChildrenOfType().First().Scale = new Vector2(2f)); + AddStep("update target", () => Player.ChildrenOfType().ForEach(LegacySkin.UpdateDrawableTarget)); + AddStep("exit player", () => Player.Exit()); + CreateTest(null); + } + + AddAssert("legacy HUD combo counter hidden", () => + { + return Player.ChildrenOfType().All(counter => !counter.IsPresent || !counter.IsAlive); + }); + } } } diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..80f09e21f3 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,11 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Platform; +using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -12,6 +16,8 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { + protected LegacySkin LegacySkin { get; private set; } + private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -19,8 +25,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(legacySkin); + LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(LegacySkin); } public class SkinProvidingPlayer : TestPlayer From f00799cc643f34b0d76ef202c1af6c8efa74472d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 18:36:46 +0300 Subject: [PATCH 032/165] Remove unused using directive ...damn it --- .../TestSceneCatchPlayerLegacySkin.cs | 1 - osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index d49470079b..7f0cbc6943 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -3,7 +3,6 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Screens; using osu.Framework.Testing; diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 80f09e21f3..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,11 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Platform; -using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Skinning; From 463774d4f2621d7e5a42de0d4e9ed9612873f3de Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 19 May 2021 19:12:16 +0700 Subject: [PATCH 033/165] add ShowPage method in WikiOverlay --- osu.Game/Overlays/WikiOverlay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 964daa3368..1022c56f26 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -62,6 +62,12 @@ namespace osu.Game.Overlays }); } + public void ShowPage(string pagePath = index_path) + { + path.Value = pagePath.Trim('/'); + Show(); + } + protected override WikiHeader CreateHeader() => new WikiHeader(); protected override void LoadComplete() From f2de28814a3388c4d1544f4af4df13688a6b467c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 17 May 2021 00:43:59 +0700 Subject: [PATCH 034/165] add and handle OpenWiki link action --- osu.Game/Online/Chat/MessageFormatter.cs | 6 +++++- osu.Game/OsuGame.cs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index b80720a0aa..1041758b0c 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -167,6 +167,9 @@ namespace osu.Game.Online.Chat case "u": case "users": return new LinkDetails(LinkAction.OpenUserProfile, mainArg); + + case "wiki": + return new LinkDetails(LinkAction.OpenWiki, string.Join('/', args.Skip(3))); } } @@ -311,7 +314,8 @@ namespace osu.Game.Online.Chat JoinMultiplayerMatch, Spectate, OpenUserProfile, - Custom + OpenWiki, + Custom, } public class Link : IComparable diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 42a49aa65e..b3b0773eff 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -309,6 +309,10 @@ namespace osu.Game ShowUser(userId); break; + case LinkAction.OpenWiki: + ShowWiki(link.Argument); + break; + default: throw new NotImplementedException($"This {nameof(LinkAction)} ({link.Action.ToString()}) is missing an associated action."); } @@ -356,6 +360,12 @@ namespace osu.Game /// The beatmap to show. public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId)); + /// + /// Show a wiki's page as an overlay + /// + /// The wiki page to show + public void ShowWiki(string path) => waitForReady(() => wikiOverlay, _ => wikiOverlay.ShowPage(path)); + /// /// Present a beatmap at song select immediately. /// The user should have already requested this interactively. From df248ea41b76cb5425b37b45c2e270d8843115ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 May 2021 11:41:53 +0300 Subject: [PATCH 035/165] Improve code readability --- .../Legacy/CatchLegacySkinTransformer.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 6c477154ac..db8c9a2e95 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -26,15 +26,20 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) { - if (!providesComboCounter || !(Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components)) + if (!providesComboCounter) return null; - // catch may provide its own combo counter; hide the default. - // todo: this should probably be done in an elegant way. - foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.Expire(); + if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + { + // catch may provide its own combo counter; hide the default. + // todo: this should probably be done in an elegant way. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.Expire(); - return components; + return components; + } + + return null; } if (!(component is CatchSkinComponent catchSkinComponent)) From f667ea3fd0045f5d365dfe20a90bd32936c630d8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 17 May 2021 21:28:14 +0300 Subject: [PATCH 036/165] Replace `AllowDefaultHUDComponentsFallback` with a temporary override at `LegacyBeatmapSkin` --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 15 ++++++++++++++- osu.Game/Skinning/LegacySkin.cs | 8 -------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 1a298576f9..91b70395f4 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -14,7 +15,6 @@ namespace osu.Game.Skinning public class LegacyBeatmapSkin : LegacySkin { protected override bool AllowManiaSkin => false; - protected override bool AllowDefaultHUDComponentsFallback => false; protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) @@ -24,6 +24,19 @@ namespace osu.Game.Skinning Configuration.AllowDefaultComboColoursFallback = false; } + public override Drawable GetDrawableComponent(ISkinComponent component) + { + if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) + { + // for now, if the beatmap skin doesn't skin the score font, fall back to current skin + // instead of potentially returning default lazer skin HUD components from here. + if (!this.HasFont(LegacyFont.Score)) + return null; + } + + return base.GetDrawableComponent(component); + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e7edba1e13..6b4f140c12 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,11 +38,6 @@ namespace osu.Game.Skinning protected virtual bool AllowManiaSkin => hasKeyTexture.Value; - /// - /// Whether this skin will fall back to default HUD components if it has no fonts for use. - /// - protected virtual bool AllowDefaultHUDComponentsFallback => true; - /// /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). /// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank). @@ -336,9 +331,6 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: - if (!this.HasFont(LegacyFont.Score) && !AllowDefaultHUDComponentsFallback) - return null; - var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { var score = container.OfType().FirstOrDefault(); From ff419af5128a3e0f2d188e10fcb05aed712ac128 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:08:56 +0300 Subject: [PATCH 037/165] Hide the combo counter content rather than full death --- .../TestSceneCatchPlayerLegacySkin.cs | 3 +- .../Legacy/CatchLegacySkinTransformer.cs | 4 +- .../Screens/Play/HUD/LegacyComboCounter.cs | 50 ++++++++++++------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 7f0cbc6943..4e56e4b4a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Screens.Play.HUD; @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(counter => !counter.IsPresent || !counter.IsAlive); + return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); }); } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index db8c9a2e95..d757f36cde 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) { // catch may provide its own combo counter; hide the default. - // todo: this should probably be done in an elegant way. + // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.Expire(); + legacyComboCounter.ContentVisible = false; return components; } diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index d64513d41e..ee00c71b0f 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD /// public class LegacyComboCounter : CompositeDrawable, ISkinnableDrawable { - public Bindable Current { get; } = new BindableInt { MinValue = 0, }; + public Bindable Current { get; } = new BindableInt { MinValue = 0 }; private uint scheduledPopOutCurrentId; @@ -32,9 +32,9 @@ namespace osu.Game.Screens.Play.HUD /// private const double rolling_duration = 20; - private Drawable popOutCount; + private readonly Drawable popOutCount; - private Drawable displayedCountSpriteText; + private readonly Drawable displayedCountSpriteText; private int previousValue; @@ -45,6 +45,13 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private ISkinSource skin { get; set; } + private readonly Container counterContainer; + + public bool ContentVisible + { + set => counterContainer.Alpha = value ? 1 : 0; + } + public LegacyComboCounter() { AutoSizeAxes = Axes.Both; @@ -55,6 +62,25 @@ namespace osu.Game.Screens.Play.HUD Margin = new MarginPadding(10); Scale = new Vector2(1.2f); + + InternalChild = counterContainer = new Container + { + AutoSizeAxes = Axes.Both, + AlwaysPresent = true, + Children = new[] + { + popOutCount = new LegacySpriteText(LegacyFont.Combo) + { + Alpha = 0, + Margin = new MarginPadding(0.05f), + Blending = BlendingParameters.Additive, + }, + displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo) + { + Alpha = 0, + }, + } + }; } /// @@ -82,20 +108,6 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(ScoreProcessor scoreProcessor) { - InternalChildren = new[] - { - popOutCount = new LegacySpriteText(LegacyFont.Combo) - { - Alpha = 0, - Margin = new MarginPadding(0.05f), - Blending = BlendingParameters.Additive, - }, - displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo) - { - Alpha = 0, - }, - }; - Current.BindTo(scoreProcessor.Combo); } @@ -105,10 +117,12 @@ namespace osu.Game.Screens.Play.HUD ((IHasText)displayedCountSpriteText).Text = formatCount(Current.Value); + counterContainer.Anchor = Anchor; + counterContainer.Origin = Origin; displayedCountSpriteText.Anchor = Anchor; displayedCountSpriteText.Origin = Origin; - popOutCount.Origin = Origin; popOutCount.Anchor = Anchor; + popOutCount.Origin = Origin; Current.BindValueChanged(combo => updateCount(combo.NewValue == 0), true); } From 265a89e5cc778e5c7054f566bdcaba16d61bf3a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:45:32 +0300 Subject: [PATCH 038/165] Fix `LegacySkinPlayerTestScene` overriden by default beatmap skin --- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..27ddd77c03 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -1,11 +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.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Skinning; +using osu.Game.Storyboards; namespace osu.Game.Tests.Visual { @@ -16,6 +22,9 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + => new LegacySkinWorkingBeatmap(beatmap, storyboard, Clock, Audio); + [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { @@ -23,6 +32,14 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(legacySkin); } + public override void SetUpSteps() + { + base.SetUpSteps(); + + // check presence of a random legacy HUD component to ensure this is using legacy skin. + AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); + } + public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] @@ -33,5 +50,15 @@ namespace osu.Game.Tests.Visual this.skinSource = skinSource; } } + + private class LegacySkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + public LegacySkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio) + : base(beatmap, storyboard, frameBasedClock, audio) + { + } + + protected override ISkin GetSkin() => new LegacyBeatmapSkin(BeatmapInfo, null, null); + } } } From 4e12a2734ccf28dae236d6c78385e79d69bf32d7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 09:51:58 +0300 Subject: [PATCH 039/165] Remove ignore attribute from now fixed test scene --- .../TestSceneCatchPlayerLegacySkin.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 4e56e4b4a7..b7cd6737b1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] - [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) @@ -33,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); + return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); }); } } From 08ee1e4853af9ac70c107ed042b37d23cd1792da Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:37:23 +0300 Subject: [PATCH 040/165] Remove HUD skin component lookup in favour of `MainHUDComponents` target system --- osu.Game/Skinning/DefaultSkin.cs | 35 +++---------------- osu.Game/Skinning/HUDSkinComponent.cs | 22 ------------ osu.Game/Skinning/LegacySkin.cs | 49 +++++++++------------------ 3 files changed, 21 insertions(+), 85 deletions(-) delete mode 100644 osu.Game/Skinning/HUDSkinComponent.cs diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index d13ddcf22b..65e8fd1b82 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Extensions; using osu.Game.IO; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Graphics; @@ -81,13 +80,12 @@ namespace osu.Game.Skinning } }) { - Children = new[] + Children = new Drawable[] { - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)), + new DefaultComboCounter(), + new DefaultScoreCounter(), + new DefaultAccuracyCounter(), + new DefaultHealthDisplay(), } }; @@ -95,29 +93,6 @@ namespace osu.Game.Skinning } break; - - case HUDSkinComponent hudComponent: - { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - return new DefaultComboCounter(); - - case HUDSkinComponents.ScoreCounter: - return new DefaultScoreCounter(); - - case HUDSkinComponents.AccuracyCounter: - return new DefaultAccuracyCounter(); - - case HUDSkinComponents.HealthDisplay: - return new DefaultHealthDisplay(); - - case HUDSkinComponents.SongProgress: - return new SongProgress(); - } - - break; - } } return null; diff --git a/osu.Game/Skinning/HUDSkinComponent.cs b/osu.Game/Skinning/HUDSkinComponent.cs deleted file mode 100644 index cc053421b7..0000000000 --- a/osu.Game/Skinning/HUDSkinComponent.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.Linq; - -namespace osu.Game.Skinning -{ - public class HUDSkinComponent : ISkinComponent - { - public readonly HUDSkinComponents Component; - - public HUDSkinComponent(HUDSkinComponents component) - { - Component = component; - } - - protected virtual string ComponentName => Component.ToString(); - - public string LookupName => - string.Join('/', new[] { "HUD", ComponentName }.Where(s => !string.IsNullOrEmpty(s))); - } -} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 6c8d6ee45a..981d8beb08 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -17,7 +17,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osuTK.Graphics; @@ -344,15 +343,22 @@ namespace osu.Game.Skinning } }) { - Children = new[] - { - // TODO: these should fallback to the osu!classic skin. - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)) ?? new DefaultComboCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)) ?? new DefaultScoreCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), - GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(), - } + Children = this.HasFont(LegacyFont.Score) + ? new Drawable[] + { + new LegacyComboCounter(), + new LegacyScoreCounter(), + new LegacyAccuracyCounter(), + new LegacyHealthDisplay(), + } + : new Drawable[] + { + // TODO: these should fallback to using osu!classic skin textures, rather than doing this. + new DefaultComboCounter(), + new DefaultScoreCounter(), + new DefaultAccuracyCounter(), + new DefaultHealthDisplay(), + } }; return skinnableTargetWrapper; @@ -360,29 +366,6 @@ namespace osu.Game.Skinning return null; - case HUDSkinComponent hudComponent: - { - if (!this.HasFont(LegacyFont.Score)) - return null; - - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - return new LegacyComboCounter(); - - case HUDSkinComponents.ScoreCounter: - return new LegacyScoreCounter(); - - case HUDSkinComponents.AccuracyCounter: - return new LegacyAccuracyCounter(); - - case HUDSkinComponents.HealthDisplay: - return new LegacyHealthDisplay(); - } - - return null; - } - case GameplaySkinComponent resultComponent: Func createDrawable = () => getJudgementAnimation(resultComponent.Component); From e5b6ad10bd6b1c9e4b1e28d85cb668ec9e5cb137 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:37:36 +0300 Subject: [PATCH 041/165] Remove no longer working combo counter hide code --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..da7d0e667d 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -22,16 +22,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is HUDSkinComponent hudComponent) - { - switch (hudComponent.Component) - { - case HUDSkinComponents.ComboCounter: - // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; - } - } - if (!(component is CatchSkinComponent catchSkinComponent)) return null; From d1272d5e13b22f4118aa7a5820bc72cc319b7312 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 12:38:06 +0300 Subject: [PATCH 042/165] Group all skinnable test scenes to one `TestSceneSkinnableHUDComponents` --- .../Visual/Gameplay/TestSceneComboCounter.cs | 35 --------- .../TestSceneSkinnableAccuracyCounter.cs | 36 --------- .../TestSceneSkinnableHUDComponents.cs | 76 +++++++++++++++++++ .../TestSceneSkinnableHealthDisplay.cs | 57 -------------- .../TestSceneSkinnableScoreCounter.cs | 41 ---------- 5 files changed, 76 insertions(+), 169 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs deleted file mode 100644 index b22af0f7ac..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs +++ /dev/null @@ -1,35 +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.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneComboCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); - } - - [Test] - public void TestComboCounterIncrementing() - { - AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10); - - AddStep("reset combo", () => scoreProcessor.Combo.Value = 0); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs deleted file mode 100644 index 6f4e6a2420..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ /dev/null @@ -1,36 +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.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); - } - - [Test] - public void TestChangingAccuracy() - { - AddStep(@"Reset all", () => scoreProcessor.Accuracy.Value = 1); - - AddStep(@"Miss :(", () => scoreProcessor.Accuracy.Value -= 0.023); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs new file mode 100644 index 0000000000..1c2f572d9e --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableHUDComponents : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + [BackgroundDependencyLoader] + private void load() + { + SetContents(() => new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + }); + } + + [Test] + public void TestScoreCounter() + { + AddStep(@"reset total score", () => scoreProcessor.TotalScore.Value = 0); + AddStep(@"increment total score", () => scoreProcessor.TotalScore.Value += 300); + AddStep(@"set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); + } + + [Test] + public void TestComboCounter() + { + AddStep(@"reset combo", () => scoreProcessor.Combo.Value = 0); + AddRepeatStep(@"increase combo", () => scoreProcessor.Combo.Value++, 10); + } + + [Test] + public void TestAccuracyCounter() + { + AddStep(@"reset accuracy", () => scoreProcessor.Accuracy.Value = 1); + AddStep(@"decrease accuracy", () => scoreProcessor.Accuracy.Value -= 0.023); + } + + [Test] + public void TestHealthDisplay() + { + AddStep(@"reset health", () => healthProcessor.Health.Value = 1); + AddRepeatStep(@"decrease hp", () => healthProcessor.Health.Value -= 0.08f, 10); + AddRepeatStep(@"decrease hp without flash", () => healthProcessor.Health.Value += 0.1f, 3); + AddRepeatStep(@"increase hp with flash", () => + { + healthProcessor.Health.Value += 0.1f; + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + }, 3); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs deleted file mode 100644 index ead27bf017..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ /dev/null @@ -1,57 +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.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableHealthDisplay : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); - AddStep(@"Reset all", delegate - { - healthProcessor.Health.Value = 1; - }); - } - - [Test] - public void TestHealthDisplayIncrementing() - { - AddRepeatStep(@"decrease hp", delegate - { - healthProcessor.Health.Value -= 0.08f; - }, 10); - - AddRepeatStep(@"increase hp without flash", delegate - { - healthProcessor.Health.Value += 0.1f; - }, 3); - - AddRepeatStep(@"increase hp with flash", delegate - { - healthProcessor.Health.Value += 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); - }, 3); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs deleted file mode 100644 index 8d633c3ca2..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ /dev/null @@ -1,41 +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.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableScoreCounter : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); - } - - [Test] - public void TestScoreCounterIncrementing() - { - AddStep(@"Reset all", () => scoreProcessor.TotalScore.Value = 0); - - AddStep(@"Hit! :D", () => scoreProcessor.TotalScore.Value += 300); - } - - [Test] - public void TestVeryLargeScore() - { - AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); - } - } -} From 076fcec3dff504fbab201aa82094b7f994a50e03 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 17:45:15 +0300 Subject: [PATCH 043/165] Revert "Remove ignore attribute from now fixed test scene" This reverts commit 4e12a2734ccf28dae236d6c78385e79d69bf32d7. --- .../TestSceneCatchPlayerLegacySkin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index b7cd6737b1..4e56e4b4a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] + [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) @@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); + return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); }); } } From e7d2f42149955a0fb623c39809f279b34f4a35a0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 18 May 2021 17:46:15 +0300 Subject: [PATCH 044/165] Revert "Merge branch 'fix-legacy-skin-test' into catch-hide-combo-workaround" This reverts commit 380d004683ad658d9ecbc408f7844eaa4c2b43c8, reversing changes made to ff419af5128a3e0f2d188e10fcb05aed712ac128. --- .../TestSceneBeatmapMetadataDisplay.cs | 151 ------------------ osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 +- .../Screens/Play/BeatmapMetadataDisplay.cs | 76 ++------- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 27 ---- 4 files changed, 15 insertions(+), 243 deletions(-) delete mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs deleted file mode 100644 index 271fbde5c3..0000000000 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ /dev/null @@ -1,151 +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 System.Linq; -using System.Threading; -using System.Threading.Tasks; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Utils; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Menu; -using osu.Game.Screens.Play; -using osuTK; - -namespace osu.Game.Tests.Visual.SongSelect -{ - public class TestSceneBeatmapMetadataDisplay : OsuTestScene - { - private BeatmapMetadataDisplay display; - - [Resolved] - private BeatmapManager manager { get; set; } - - [Cached(typeof(BeatmapDifficultyCache))] - private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); - - [Test] - public void TestLocal([Values("Beatmap", "Some long title and stuff")] - string title, - [Values("Trial", "Some1's very hardest difficulty")] - string version) - { - showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap - { - BeatmapInfo = - { - Metadata = new BeatmapMetadata - { - Title = title, - }, - Version = version, - StarDifficulty = RNG.NextDouble(0, 10), - } - })); - } - - [Test] - public void TestDelayedStarRating() - { - AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); - - showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap - { - BeatmapInfo = - { - Metadata = new BeatmapMetadata - { - Title = "Heavy beatmap", - }, - Version = "10k objects", - StarDifficulty = 99.99f, - } - })); - - AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); - } - - [Test] - public void TestRandomFromDatabase() - { - showMetadataForBeatmap(() => - { - var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); - if (allBeatmapSets.Count == 0) - return manager.DefaultBeatmap; - - var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; - var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; - - return manager.GetWorkingBeatmap(randomBeatmap); - }); - } - - private void showMetadataForBeatmap(Func getBeatmap) - { - AddStep("setup display", () => - { - var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); - - OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; - - Remove(testDifficultyCache); - - Children = new Drawable[] - { - testDifficultyCache, - display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Alpha = 0f, - } - }; - - display.FadeIn(400, Easing.OutQuint); - }); - - AddWaitStep("wait a bit", 5); - - AddStep("finish loading", () => display.Loading = false); - } - - private class TestBeatmapDifficultyCache : BeatmapDifficultyCache - { - private TaskCompletionSource calculationBlocker; - - private bool blockCalculation; - - public bool BlockCalculation - { - get => blockCalculation; - set - { - if (value == blockCalculation) - return; - - blockCalculation = value; - - if (value) - calculationBlocker = new TaskCompletionSource(); - else - calculationBlocker?.SetResult(false); - } - } - - public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) - { - if (blockCalculation) - await calculationBlocker.Task; - - return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); - } - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 6ed623d0c0..53d82c385d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, - [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) + public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, + CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index fd1150650c..c56344a8fb 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -15,7 +14,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -27,7 +25,7 @@ namespace osu.Game.Screens.Play { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable logoFacade; + private readonly Drawable facade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -43,24 +41,19 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) { this.beatmap = beatmap; - this.logoFacade = logoFacade; + this.facade = facade; this.mods = new Bindable>(); this.mods.BindTo(mods); } - private IBindable starDifficulty; - - private FillFlowContainer versionFlow; - private StarRatingDisplay starRatingDisplay; - [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache difficultyCache) + private void load() { - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -73,7 +66,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - logoFacade.With(d => + facade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; @@ -114,30 +107,16 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - versionFlow = new FillFlowContainer + new OsuSpriteText { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), Origin = Anchor.TopCentre, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5f), - Margin = new MarginPadding { Bottom = 40 }, - Children = new Drawable[] + Anchor = Anchor.TopCentre, + Margin = new MarginPadding { - new OsuSpriteText - { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - starRatingDisplay = new StarRatingDisplay(default) - { - Alpha = 0f, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - } - } + Bottom = 40 + }, }, new GridContainer { @@ -180,38 +159,9 @@ namespace osu.Game.Screens.Play } }; - starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); - Loading = true; } - protected override void LoadComplete() - { - base.LoadComplete(); - - if (starDifficulty.Value != null) - { - starRatingDisplay.Current.Value = starDifficulty.Value.Value; - starRatingDisplay.Show(); - } - else - { - starRatingDisplay.Hide(); - - starDifficulty.ValueChanged += d => - { - Debug.Assert(d.NewValue != null); - - starRatingDisplay.Current.Value = d.NewValue.Value; - - versionFlow.AutoSizeDuration = 300; - versionFlow.AutoSizeEasing = Easing.OutQuint; - - starRatingDisplay.FadeIn(300, Easing.InQuint); - }; - } - } - private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 25c1ae26f9..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -1,17 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.IO.Stores; -using osu.Framework.Testing; -using osu.Framework.Timing; -using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Skinning; -using osu.Game.Storyboards; namespace osu.Game.Tests.Visual { @@ -24,9 +18,6 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); - protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) - => new LegacySkinWorkingBeatmap(beatmap, storyboard, Clock, Audio); - [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { @@ -34,14 +25,6 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(LegacySkin); } - public override void SetUpSteps() - { - base.SetUpSteps(); - - // check presence of a random legacy HUD component to ensure this is using legacy skin. - AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); - } - public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] @@ -52,15 +35,5 @@ namespace osu.Game.Tests.Visual this.skinSource = skinSource; } } - - private class LegacySkinWorkingBeatmap : ClockBackedTestWorkingBeatmap - { - public LegacySkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio) - : base(beatmap, storyboard, frameBasedClock, audio) - { - } - - protected override ISkin GetSkin() => new LegacyBeatmapSkin(BeatmapInfo, null, null); - } } } From 013fe4928fe2177802a99bcd1715d9d31077021e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 08:48:21 +0300 Subject: [PATCH 045/165] Unrevert irrelevant changes --- .../TestSceneBeatmapMetadataDisplay.cs | 151 ++++++++++++++++++ osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 +- .../Screens/Play/BeatmapMetadataDisplay.cs | 76 +++++++-- 3 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs new file mode 100644 index 0000000000..271fbde5c3 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -0,0 +1,151 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.SongSelect +{ + public class TestSceneBeatmapMetadataDisplay : OsuTestScene + { + private BeatmapMetadataDisplay display; + + [Resolved] + private BeatmapManager manager { get; set; } + + [Cached(typeof(BeatmapDifficultyCache))] + private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); + + [Test] + public void TestLocal([Values("Beatmap", "Some long title and stuff")] + string title, + [Values("Trial", "Some1's very hardest difficulty")] + string version) + { + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = title, + }, + Version = version, + StarDifficulty = RNG.NextDouble(0, 10), + } + })); + } + + [Test] + public void TestDelayedStarRating() + { + AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); + + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = "Heavy beatmap", + }, + Version = "10k objects", + StarDifficulty = 99.99f, + } + })); + + AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); + } + + [Test] + public void TestRandomFromDatabase() + { + showMetadataForBeatmap(() => + { + var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (allBeatmapSets.Count == 0) + return manager.DefaultBeatmap; + + var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; + var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; + + return manager.GetWorkingBeatmap(randomBeatmap); + }); + } + + private void showMetadataForBeatmap(Func getBeatmap) + { + AddStep("setup display", () => + { + var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); + + OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; + + Remove(testDifficultyCache); + + Children = new Drawable[] + { + testDifficultyCache, + display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + } + }; + + display.FadeIn(400, Easing.OutQuint); + }); + + AddWaitStep("wait a bit", 5); + + AddStep("finish loading", () => display.Loading = false); + } + + private class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + private TaskCompletionSource calculationBlocker; + + private bool blockCalculation; + + public bool BlockCalculation + { + get => blockCalculation; + set + { + if (value == blockCalculation) + return; + + blockCalculation = value; + + if (value) + calculationBlocker = new TaskCompletionSource(); + else + calculationBlocker?.SetResult(false); + } + } + + public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + { + if (blockCalculation) + await calculationBlocker.Task; + + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + } + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 53d82c385d..6ed623d0c0 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, - CancellationToken cancellationToken = default) + public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index c56344a8fb..fd1150650c 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -25,7 +27,7 @@ namespace osu.Game.Screens.Play { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable facade; + private readonly Drawable logoFacade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -41,19 +43,24 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) { this.beatmap = beatmap; - this.facade = facade; + this.logoFacade = logoFacade; this.mods = new Bindable>(); this.mods.BindTo(mods); } + private IBindable starDifficulty; + + private FillFlowContainer versionFlow; + private StarRatingDisplay starRatingDisplay; + [BackgroundDependencyLoader] - private void load() + private void load(BeatmapDifficultyCache difficultyCache) { - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap.BeatmapInfo.Metadata; AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -66,7 +73,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - facade.With(d => + logoFacade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; @@ -107,16 +114,30 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - new OsuSpriteText + versionFlow = new FillFlowContainer { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, - Margin = new MarginPadding + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + Margin = new MarginPadding { Bottom = 40 }, + Children = new Drawable[] { - Bottom = 40 - }, + new OsuSpriteText + { + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + starRatingDisplay = new StarRatingDisplay(default) + { + Alpha = 0f, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } }, new GridContainer { @@ -159,9 +180,38 @@ namespace osu.Game.Screens.Play } }; + starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); + Loading = true; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (starDifficulty.Value != null) + { + starRatingDisplay.Current.Value = starDifficulty.Value.Value; + starRatingDisplay.Show(); + } + else + { + starRatingDisplay.Hide(); + + starDifficulty.ValueChanged += d => + { + Debug.Assert(d.NewValue != null); + + starRatingDisplay.Current.Value = d.NewValue.Value; + + versionFlow.AutoSizeDuration = 300; + versionFlow.AutoSizeEasing = Easing.OutQuint; + + starRatingDisplay.FadeIn(300, Easing.InQuint); + }; + } + } + private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text) From bc1cad0963c4624ec3cd09acd3124ebc349c8e7a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 00:28:26 +0700 Subject: [PATCH 046/165] change header breadcrumb when page change --- osu.Game/Overlays/Wiki/WikiHeader.cs | 31 ++++++++++++++++++++++++++++ osu.Game/Overlays/WikiOverlay.cs | 1 + 2 files changed, 32 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 91377c63da..f5010d82b6 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -1,17 +1,48 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Wiki { public class WikiHeader : BreadcrumbControlOverlayHeader { private const string index_page_string = "index"; + private const string index_path = "Main_Page"; + + public Bindable WikiPageData = new Bindable(); public WikiHeader() { TabControl.AddItem(index_page_string); + Current.Value = index_page_string; + + WikiPageData.BindValueChanged(onWikiPageChange); + } + + private void onWikiPageChange(ValueChangedEvent e) + { + if (e.NewValue == null) + return; + + TabControl.Clear(); + Current.Value = null; + + TabControl.AddItem(index_page_string); + + if (e.NewValue.Path == index_path) + { + Current.Value = index_page_string; + return; + } + + if (e.NewValue.Subtitle != null) + TabControl.AddItem(e.NewValue.Subtitle); + + TabControl.AddItem(e.NewValue.Title); + Current.Value = e.NewValue.Title; } protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 1022c56f26..366923a4f1 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -74,6 +74,7 @@ namespace osu.Game.Overlays { base.LoadComplete(); path.BindValueChanged(onPathChanged); + wikiData.BindTo(Header.WikiPageData); } protected override void PopIn() From a730349629c5b3f39a0107980a39bbb5161125cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:18:54 +0300 Subject: [PATCH 047/165] Remove the ignore attribute once again --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 4e56e4b4a7..79394c1f19 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -20,7 +20,6 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); [Test] - [Ignore("HUD components broken, remove when fixed.")] public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin) { if (withModifiedSkin) From e20c25b0d95b782126d1d49d5c47ce89ecc1a7f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:32:16 +0300 Subject: [PATCH 048/165] Fix miswritten test assertion --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 79394c1f19..b7cd6737b1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("legacy HUD combo counter hidden", () => { - return Player.ChildrenOfType().All(c => !c.ChildrenOfType().Single().IsPresent); + return Player.ChildrenOfType().All(c => c.ChildrenOfType().Single().Alpha == 0f); }); } } From a4d52a7f529c1693aace27ed772d2e9805f236a6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:37:22 +0300 Subject: [PATCH 049/165] =?UTF-8?q?Use=20switch=E2=80=94case=20instead?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Legacy/CatchLegacySkinTransformer.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index d757f36cde..46a9cc6f70 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -24,19 +24,25 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (component is SkinnableTargetComponent targetComponent && targetComponent.Target == SkinnableTarget.MainHUDComponents) + if (component is SkinnableTargetComponent targetComponent) { - if (!providesComboCounter) - return null; - - if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + switch (targetComponent.Target) { - // catch may provide its own combo counter; hide the default. - // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. - foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.ContentVisible = false; + case SkinnableTarget.MainHUDComponents: + if (!providesComboCounter) + break; - return components; + if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + { + // catch may provide its own combo counter; hide the default. + // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. + foreach (var legacyComboCounter in components.OfType()) + legacyComboCounter.ContentVisible = false; + + return components; + } + + break; } return null; From d8efcc0793313d0e09d5f566467805021cb645eb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:44:53 +0300 Subject: [PATCH 050/165] Remove drive-by change --- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 907a37f4b9..c186525757 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -12,8 +12,6 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { - protected LegacySkin LegacySkin { get; private set; } - private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -21,8 +19,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(LegacySkin); + var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(legacySkin); } public class SkinProvidingPlayer : TestPlayer From 3d99b89633b622b93a59423f72660d2af57eec01 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 00:03:10 +0300 Subject: [PATCH 051/165] Add back actually needed change *no comment* --- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..907a37f4b9 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual [TestFixture] public abstract class LegacySkinPlayerTestScene : PlayerTestScene { + protected LegacySkin LegacySkin { get; private set; } + private ISkinSource legacySkinSource; protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); @@ -19,8 +21,8 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(OsuGameBase game, SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); - legacySkinSource = new SkinProvidingContainer(legacySkin); + LegacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + legacySkinSource = new SkinProvidingContainer(LegacySkin); } public class SkinProvidingPlayer : TestPlayer From 1578b0462d10a9c63d1f378f080c07fa27cf43a0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 13:58:17 +0700 Subject: [PATCH 052/165] add showParentPage method --- osu.Game/Overlays/WikiOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 366923a4f1..1efa84d026 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -62,6 +63,12 @@ namespace osu.Game.Overlays }); } + private void showParentPage() + { + var parentPath = string.Join("/", path.Value.Split('/').SkipLast(1)); + ShowPage(parentPath); + } + public void ShowPage(string pagePath = index_path) { path.Value = pagePath.Trim('/'); From 9c824ece1b068c91d84cab4fe6508336090ce5c9 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 14:00:22 +0700 Subject: [PATCH 053/165] handle page change when clicking breadcrumb --- osu.Game/Overlays/Wiki/WikiHeader.cs | 22 +++++++++++++++++++++- osu.Game/Overlays/WikiOverlay.cs | 6 +++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index f5010d82b6..6b8cba48b4 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; @@ -12,7 +14,10 @@ namespace osu.Game.Overlays.Wiki private const string index_page_string = "index"; private const string index_path = "Main_Page"; - public Bindable WikiPageData = new Bindable(); + public readonly Bindable WikiPageData = new Bindable(); + + public Action ShowIndexPage; + public Action ShowParentPage; public WikiHeader() { @@ -20,6 +25,7 @@ namespace osu.Game.Overlays.Wiki Current.Value = index_page_string; WikiPageData.BindValueChanged(onWikiPageChange); + Current.BindValueChanged(onCurrentChange); } private void onWikiPageChange(ValueChangedEvent e) @@ -45,6 +51,20 @@ namespace osu.Game.Overlays.Wiki Current.Value = e.NewValue.Title; } + private void onCurrentChange(ValueChangedEvent e) + { + if (e.NewValue == TabControl.Items.LastOrDefault()) + return; + + if (e.NewValue == index_page_string) + { + ShowIndexPage?.Invoke(); + return; + } + + ShowParentPage?.Invoke(); + } + protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/wiki"); protected override OverlayTitle CreateTitle() => new WikiHeaderTitle(); diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 1efa84d026..5505eeac68 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -75,7 +75,11 @@ namespace osu.Game.Overlays Show(); } - protected override WikiHeader CreateHeader() => new WikiHeader(); + protected override WikiHeader CreateHeader() => new WikiHeader + { + ShowIndexPage = () => ShowPage(), + ShowParentPage = showParentPage, + }; protected override void LoadComplete() { From 11099702780ab3f7366781aad49aa940eb7261d1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 14:24:28 +0700 Subject: [PATCH 054/165] add more test for Wiki Header --- .../Visual/Online/TestSceneWikiHeader.cs | 83 ++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 51d8abc516..863fa48ddf 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -1,8 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Wiki; @@ -13,13 +18,85 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange); - public TestSceneWikiHeader() + [Cached] + private readonly Bindable wikiPageData = new Bindable(new APIWikiPage { - Child = new WikiHeader + Title = "Main Page", + Path = "Main_Page", + }); + + private TestHeader header; + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = header = new TestHeader { Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, + ShowIndexPage = dummyShowIndexPage, + ShowParentPage = dummyShowParentPage, }; + wikiPageData.BindTo(header.WikiPageData); + }); + + [Test] + public void TestWikiHeader() + { + AddAssert("Current is index", () => checkCurrent("index")); + + AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage + { + Title = "Welcome", + Path = "Welcome" + }); + AddAssert("Current is welcome", () => checkCurrent("Welcome")); + AddAssert("Check breadcrumb", checkBreadcrumb); + + AddStep("Change current to index", () => header.Current.Value = "index"); + AddAssert("Current is index", () => checkCurrent("index")); + + AddStep("Change wiki page data", () => wikiPageData.Value = new APIWikiPage + { + Title = "Developers", + Path = "People/The_Team/Developers", + Subtitle = "The Team", + }); + AddAssert("Current is 'Developers'", () => checkCurrent("Developers")); + AddAssert("Check breadcrumb", checkBreadcrumb); + + AddStep("Change current to 'The Team'", () => header.Current.Value = "The Team"); + AddAssert("Current is 'The Team'", () => checkCurrent("The Team")); + AddAssert("Check breadcrumb", checkBreadcrumb); + } + + private bool checkCurrent(string expectedCurrent) => header.Current.Value == expectedCurrent; + + private bool checkBreadcrumb() + { + var result = header.TabControlItems.Contains(wikiPageData.Value.Title); + + if (wikiPageData.Value.Subtitle != null) + result = header.TabControlItems.Contains(wikiPageData.Value.Subtitle) && result; + + return result; + } + + private void dummyShowIndexPage() => wikiPageData.SetDefault(); + + private void dummyShowParentPage() + { + wikiPageData.Value = new APIWikiPage + { + Path = "People/The_Team", + Title = "The Team", + Subtitle = "People" + }; + } + + private class TestHeader : WikiHeader + { + public IReadOnlyList TabControlItems => TabControl.Items; } } } From ae0949fc145d0f8aebb8424ec4ac99adf487c3a1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:20:04 +0700 Subject: [PATCH 055/165] add main page in wiki overlay --- osu.Game/Overlays/WikiOverlay.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 5505eeac68..6e07de71d0 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -54,13 +54,24 @@ namespace osu.Game.Overlays private void onSuccess(APIWikiPage response) { wikiData.Value = response; - LoadDisplay(new WikiMarkdownContainer + + if (response.Layout == "main_page") { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - CurrentPath = $"{path.Value}/", - Text = response.Markdown, - }); + LoadDisplay(new WikiMainPage + { + Markdown = response.Markdown + }); + } + else + { + LoadDisplay(new WikiMarkdownContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CurrentPath = $"{path.Value}/", + Text = response.Markdown, + }); + } } private void showParentPage() From 6257504bb6170bfb186f2dad856f96fa9052e284 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:37:14 +0700 Subject: [PATCH 056/165] add padding spacing --- osu.Game/Overlays/WikiOverlay.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 6e07de71d0..fa8ba66bcd 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -59,7 +59,12 @@ namespace osu.Game.Overlays { LoadDisplay(new WikiMainPage { - Markdown = response.Markdown + Markdown = response.Markdown, + Padding = new MarginPadding + { + Vertical = 20, + Horizontal = 50, + }, }); } else @@ -70,6 +75,13 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, CurrentPath = $"{path.Value}/", Text = response.Markdown, + DocumentMargin = new MarginPadding(0), + DocumentPadding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50, + }, }); } } From 1ad3aee126e29aac93fb28089460c1acbe0e589c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 20 May 2021 17:49:20 +0700 Subject: [PATCH 057/165] add article page test wiki overlay --- .../Visual/Online/TestSceneWikiOverlay.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 371be8a003..da4bf82948 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -21,11 +21,18 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestMainPage() { - setUpNewsResponse(responseExample); + setUpWikiResponse(responseMainPage); AddStep("Show Main Page", () => wiki.Show()); } - private void setUpNewsResponse(APIWikiPage r) + [Test] + public void TestArticlePage() + { + setUpWikiResponse(responseArticlePage); + AddStep("Show Article Page", () => wiki.ShowPage("Interface")); + } + + private void setUpWikiResponse(APIWikiPage r) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => @@ -39,14 +46,27 @@ namespace osu.Game.Tests.Visual.Online }); // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page - private APIWikiPage responseExample => new APIWikiPage + private APIWikiPage responseMainPage => new APIWikiPage { Title = "Main Page", Layout = "main_page", Path = "Main_Page", Locale = "en", Subtitle = null, - Markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + Markdown = + "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n", + }; + + // From https://osu.ppy.sh/api/v2/wiki/en/Interface + private APIWikiPage responseArticlePage => new APIWikiPage + { + Title = "Interface", + Layout = "markdown_page", + Path = "Interface", + Locale = "en", + Subtitle = null, + Markdown = + "# Interface\n\n![](img/intro-screen.jpg \"Introduction screen\")\n\n## Main Menu\n\n![](img/main-menu.jpg \"Main Menu\")\n\nThe [osu!cookie](/wiki/Glossary#cookie) \\[1\\] pulses according to the [BPM](/wiki/Beatmapping/Beats_per_minute) of any song currently playing on the main menu. In addition, bars will extend out of the osu!cookie in accordance to the song's volume. If no song is playing, it pulses at a slow 60 BPM. The elements of the main menu are as follows:\n\n- \\[2\\] Click Play (`P`) or the logo to switch to the Solo mode song selection screen.\n- \\[3\\] Click Edit (`E`) to open the Editor mode song selection screen.\n- \\[4\\] Click Options (`O`) to go to the Options screen.\n- \\[5\\] Click Exit (`Esc`) to exit osu!.\n- \\[6\\] A random useful tip is displayed below the menu.\n- \\[7\\] In the lower-left is a link to the osu! website, as well as copyright information.\n- \\[8\\] Connection result to [Bancho](/wiki/Glossary#bancho)! In this picture it is not shown, but the connection result looks like a chain link.\n- \\[9\\] In the bottom right are the chat controls for the extended [chat window](/wiki/Chat_Console) (called \"Player List\" here) and the regular chat window (`F9` & `F8`, respectively).\n- \\[10\\] In the upper right is the osu! jukebox which plays the songs in random order. The top shows the song currently playing. The buttons, from left to right, do as follows:\n - Previous Track\n - Play\n - Pause\n - Stop (the difference between Play and Stop is that Stop will reset the song to the beginning, while Pause simply pauses it)\n - Next Track\n - View Song Info. This toggles the top bar showing the song info between being permanent and temporary. When permanent, the info bar will stay visible until it fades out with the rest of the UI. When temporary, it will disappear a little while after a song has been chosen. It will stay hidden until it is toggled again, or another song plays.\n- \\[11\\] The number of beatmaps you have available, how long your osu!client has been running, and your system clock.\n- \\[12\\] Your profile, click on it to display the User Options (see below).\n\n## User Options\n\n![](img/user-options.jpg \"User Options\")\n\nAccess this screen by clicking your profile at the top left of the main menu. You cannot access the Chat Consoles while viewing the user option screen. You can select any item by pressing the corresponding number on the option:\n\n1. `View Profile`: Opens up your profile page in your default web browser.\n2. `Sign Out`: Sign out of your account (after signing out, the [Options](/wiki/Options) sidebar will prompt you to sign in).\n3. `Change Avatar`: Open up the edit avatar page in your default web browser.\n4. `Close`: Close this dialog\n\n## Play Menu\n\n![](img/play-menu.jpg \"Play Menu\")\n\n- Click `Solo` (`P`) to play alone.\n- Click `Multi` (`M`) to play with other people. You will be directed to the [Multi](/wiki/Multi) Lobby (see below).\n- Click `Back` to return to the main menu.\n\n## Multi Lobby\n\n*Main page: [Multi](/wiki/Multi)*\n\n![](img/multi-lobby.jpg \"Multi Lobby\")\n\n![](img/multi-room.jpg \"Multi Host\")\n\n1. Your rank in the match. This is also shown next to your name.\n2. Your profile information.\n3. The jukebox.\n4. Player list - displays player names, their rank (host or player), their [mods](/wiki/Game_modifier) activated (if any, see \\#7), their osu! ranking, and their team (if applicable).\n5. The name of the match and the password settings.\n6. The beatmap selected. It shows the beatmap as it would in the solo song selection screen.\n7. The [mods](/wiki/Game_modifier) that you have activated (see #12), as well as the option to select them. The option marked \"Free Mods\" toggles whether or not players can select their own mods. If yes, they can pick any combination of mods *except for speed-altering mods like [Double Time](/wiki/Game_modifier/Double_Time)*. If no, the host decides what mods will be used. The host can pick speed-altering mods regardless of whether or not Free Mods is turned on.\n8. The team mode and win conditions.\n9. The ready button.\n10. The [chat console](/wiki/Chat_Console).\n11. The leave button.\n12. Where your activated mods appear.\n\n## Song Selection Screen\n\n![](img/song-selection.jpg \"Song Selection\")\n\nYou can identify the current mode selected by either looking at the icon in the bottom left, above Mode, or by looking at the transparent icon in the center of the screen. These are the four you will see:\n\n- ![](/wiki/shared/mode/osu.png) is [osu!](/wiki/Game_mode/osu!)\n- ![](/wiki/shared/mode/taiko.png) is [osu!taiko](/wiki/Game_mode/osu!taiko)\n- ![](/wiki/shared/mode/catch.png) is [osu!catch](/wiki/Game_mode/osu!catch)\n- ![](/wiki/shared/mode/mania.png) is [osu!mania](/wiki/Game_mode/osu!mania)\n\nBefore continuing on, this screen has too many elements to note with easily, noticeable numbers. The subsections below will focus on one part of the screen at a time, starting from the top down and left to right.\n\n### Beatmap Information\n\n![](img/metadata-comparison.jpg)\n\n![](img/beatmap-metadata.jpg)\n\nThis area displays **information on the beatmap difficulty currently selected.** By default, the beatmap whose song is heard in the osu! jukebox is selected when entering the selection screen. In the top left is the ranked status of the beatmap. The title is next. Normally, the romanised title is shown, but if you select `Prefer metadata in original language` in the [Options](/wiki/Options), it will show the Unicode title; this is shown in the upper picture. The beatmapper is also shown, and beatmap information is shown below. From left to right, the values are as follows:\n\n- **Length**: The total length of the beatmap, from start to finish and including breaks. Not to be confused with [drain time](/wiki/Glossary#drain-time).\n- **BPM**: The BPM of the beatmap. If (like in the lower picture) there are two BPMS and one in parentheses, this means that the BPM changes throughout the song. It shows the slowest and fastest BPMs, and the value in parentheses is the BPM at the start of the beatmap.\n- **Objects**: The total amount of [hit objects](/wiki/Hit_Objects) in the beatmap.\n- **Circles**: The total amount of hit circles in the beatmap.\n- **Sliders**: The total amount of sliders in the beatmap.\n- **Spinners**: The total amount of spinners in the beatmap.\n- **OD**: The Overall Difficulty of the beatmap.\n- **HP**: The drain rate of your HP. In osu!, this is how much of an HP loss you receive upon missing a note, how fast the life bar idly drains, and how much HP is received for hitting a note. In osu!mania, this is the same except there is no idle HP drain. In osu!taiko, this determines how slowly the HP bar fills and how much HP is lost when a note is missed. osu!catch is the same as osu!.\n- **Stars**: The star difficulty of the beatmap. This is graphically visible in the beatmap rectangle itself.\n\n### Group and Sort\n\n![](img/beatmap-filters.jpg)\n\nClick on one of the tabs to **sort your song list according to the selected criterion**.\n\n**Group** - Most options organize beatmaps into various expandable groups:\n\n- `No grouping` - Beatmaps will not be grouped but will still be sorted in the order specified by Sort.\n- `By Difficulty` - Beatmaps will be grouped by their star difficulty, rounded to the nearest whole number.\n- `By Artist` - Beatmaps will be grouped by the artist's first character of their name.\n- `Recently Played` - Beatmaps will be grouped by when you last played them.\n- `Collections` - This will show the collections you have created. *Note that this will hide beatmaps not listed in a collection!*\n- `By BPM` - Beatmaps will be grouped according to BPM in multiples of 60, starting at 120.\n- `By Creator` - Beatmaps will be grouped by the beatmap creator's name's first character.\n- `By Date Added` - Beatmaps will be grouped according to when they were added, from today to 4+ months ago.\n- `By Length` - Beatmaps will be grouped according to their length: 1 minute or less, 2 minutes or less, 3, 4, 5, and 10.\n- `By Mode` - Beatmaps will be grouped according to their game mode.\n- `By Rank Achieved` - Beatmaps will be sorted by the highest rank achieved on them.\n- `By Title` - Beatmaps will be grouped by the first letter of their title.\n- `Favourites` - Only beatmaps you have favorited online will be shown.\n- `My Maps` - Only beatmaps you have mapped (that is, whose creator matches your profile name) will be shown.\n- `Ranked Status` - Beatmaps will be grouped by their ranked status: ranked, pending, not submitted, unknown, or loved.\n\nThe first five groupings are available in tabs below Group and Sort.\n\n**Sort** - Sorts beatmaps in a certain order\n\n- `By Artist` - Beatmaps will be sorted alphabetically by the artist's name's first character.\n- `By BPM` - Beatmaps will be sorted lowest to highest by their BPM. For maps with multiple BPMs, the highest will be used.\n- `By Creator` - Beatmaps will be sorted alphabetically by the creator's name's first character.\n- `By Date Added` - Beatmaps will be sorted from oldest to newest by when they were added.\n- `By Difficulty` - Beatmaps will be sorted from easiest to hardest by star difficulty. *Note that this will split apart mapsets!*\n- `By Length` - Beatmaps will be sorted from shortest to longest by length.\n- `By Rank Achieved` - Beatmaps will be sorted from poorest to best by the highest rank achieved on them.\n- `By Title` - Beatmaps will be sorted alphabetically by the first character of their name.\n\n### Search\n\n![](img/search-bar.jpg)\n\n*Note: You cannot have the chat console or the options sidebar open if you want to search; otherwise, anything you type will be perceived as chat text or as an options search query.*\n\nOnly beatmaps that match the criteria of your search will be shown. By default, any search will be matched against the beatmaps' artists, titles, creators, and tags.\n\nIn addition to searching these fields, you can use filters to search through other metadata by combining one of the supported filters with a comparison to a value (for example, `ar=9`).\n\nSupported filters:\n\n- `artist`: Name of the artist\n- `creator`: Name of the beatmap creator\n- `ar`: Approach Rate\n- `cs`: Circle Size\n- `od`: Overall Difficulty\n- `hp`: HP Drain Rate\n- `keys`: Number of keys (osu!mania and converted beatmaps only)\n- `stars`: Star Difficulty\n- `bpm`: Beats per minute\n- `length`: Length in seconds\n- `drain`: Drain Time in seconds\n- `mode`: Mode. Value can be `osu`, `taiko`, `catchthebeat`, or `mania`, or `o`/`t`/`c`/`m` for short.\n- `status`: Ranked status. Value can be `ranked`, `approved`, `pending`, `notsubmitted`, `unknown`, or `loved`, or `r`/`a`/`p`/`n`/`u`/`l` for short.\n- `played`: Time since last played in days\n- `unplayed`: Shows only unplayed maps. A comparison with no set value must be used. The comparison itself is ignored.\n- `speed`: Saved osu!mania scroll speed. Always 0 for unplayed maps or if the [Remember osu!mania scroll speed per beatmap](/wiki/Options#gameplay) option is off\n\nSupported comparisons:\n\n- `=` or `==`: Equal to\n- `!=`: Not equal to\n- `<`: Less than\n- `>`: Greater than\n- `<=`: Less than or equal to\n- `>=`: Greater than or equal to\n\nYou may also enter a beatmap or beatmapset ID in your search to get a single result.\n\n### Rankings\n\n![](img/leaderboards.jpg)\n\n A variety of things can appear in this space:\n\n- A \"Not Submitted\" box denotes a beatmap that has not been uploaded to the osu! site using the Beatmap Submission System or was deleted by the mapper.\n- An \"Update to latest version\" box appears if there is a new version of the beatmap available for download. Click on the button to update.\n - **Note:** Once you update the beatmap, it cannot be reversed. If you want to preserve the older version for some reason (say, to keep scores), then do not update.\n- A \"Latest pending version\" box appears means that the beatmap has been uploaded to the osu!website but is not ranked yet.\n- If replays matching the view setting of the beatmap exist, they will be displayed instead of a box denoting the ranked/played status of the beatmap. This is shown in the above picture.\n - Under public rankings (e.g. Global, Friends, etc.), your high score will be shown at the bottom, as well as your rank on the leaderboard.\n- A \"No records set!\" box means that there are no replays for the current view setting (this is typically seen in the Local view setting if you just downloaded or edited the beatmap).\n - Note: Scores for Multi are not counted as records.\n\nThese are the view settings:\n\n- Local Ranking\n- Country Ranking\\*\n- Global Ranking\n- Global Ranking (Selected Mods)\\*\n- Friend Ranking\\*\n\n\\*Requires you to be an [osu!supporter](/wiki/osu!supporter) to access them.\n\nClick the word bubble icon to call up the **Quick Web Access** screen for the selected beatmap:\n\n- Press `1` or click the `Beatmap Listing/Scores` button and your default internet browser will pull up the Beatmap Listing and score page of the beatmap set the selected beatmap belongs to.\n- Press `2` or click `Beatmap Modding` and your default internet browser will pull up the modding page of the beatmap set the selected beatmap belongs to.\n- Press `3` or `Esc` or click `Cancel` to return to the Song Selection Screen.\n\nWhile you are on the Quick Web Access Screen, you cannot access the Chat and Extended Chat Consoles.\n\n### Song\n\n![](img/beatmap-cards.jpg)\n\nThe song list displays all available beatmaps. Different beatmaps may have different coloured boxes:\n\n- **Pink**: This beatmap has not been played yet.\n- **Orange**: At least one beatmap from the beatmapset has been completed.\n- **Light Blue**: Other beatmaps in the same set, shown when a mapset is expanded.\n- **White**: Currently selected beatmap.\n\nYou can navigate the beatmap list by using the mouse wheel, using the up and down arrow keys, dragging it while holding the left mouse button or clicking the right mouse button (previously known as Absolute Scrolling), which will move the scroll bar to your mouse's Y position. Click on a box to select that beatmap and display its information on the upper left, high scores (if any) on the left and, if you've cleared it, the letter grade of the highest score you've achieved. Click the box again, press `Enter` or click the osu!cookie at the lower right to begin playing the beatmap.\n\n### Gameplay toolbox\n\n![](img/game-mode-selector.jpg \"List of available game modes\")\n\n![](img/gameplay-toolbox.jpg)\n\nThis section can be called the gameplay toolbox. We will cover each button's use from left to right.\n\nPress `Esc` or click the `Back` button to return to main menu.\n\nClick on the `Mode` button to open up a list of gameplay modes available on osu!. Click on your desired gameplay mode and osu! will switch to that gameplay mode style - the scoreboard will change accordingly. Alternatively, you can press `Ctrl` and `1` (osu!), `2` (osu!taiko), `3` (osu!catch), or `4` (osu!mania) to change the gamemode.\n\nThe background transparent icon and the \"Mode\" box will change to depict what mode is currently selected.\n\n![](img/game-modifiers.jpg \"Mod Selection Screen\")\n\nClick the `Mods` button or press `F1` to open the **[Mod Selection Screen](/wiki/Game_modifier)**.\n\nIn this screen, you can apply modifications (\"mods\" for short) to gameplay. Some mods lower difficulty and apply a multiplier that lowers the score you achieve. Conversely, some mods increase the difficulty, but apply a multiplier that increases the score you achieve. Finally, some mods modify gameplay in a different way. [Relax](/wiki/Game_modifier/Relax) and [Auto Pilot](/wiki/Game_modifier/Autopilot) fall in that category.\n\nPlace your mouse on a mod's icon to see a short description of its effect. Click on an icon to select or deselect that mod. Some mods, like Double Time, have multiple variations; click on the mod again to cycle through. The score multiplier value displays the combined effect the multipliers of the mod(s) of you have selected will have on your score. Click `Reset all mods` or press `1` to deselect all currently selected mods. Click `Close` or press `2` or `Esc` to return to the Song Selection Screen.\n\nWhile you are on the Mod Selection Screen, you cannot access the Chat and Extended Chat Consoles. In addition, skins can alter the text and/or icon of the mods, but the effects will still be the same.\n\nClick the `Random` button or press `F2` to have the game **randomly scroll through all of your beatmaps and pick one.** You cannot select a beatmap yourself until it has finished scrolling.\n\n*Note: You can press `Shift` + the `Random` button or `F2` to go back to the beatmap you had selected before you randomized your selection.*\n\n![](img/beatmap-options.jpg \"Possible commands for a beatmap\")\n\nClick the `Beatmap Options` button, press `F3` or right-click your mouse while hovering over the beatmap to call up the **Beatmap Options Menu for options on the currently selected beatmap**.\n\n- Press `1` or click the `Manage Collections` button to bring up the Collections screen - here, you can manage pre-existing collections, as well as add or remove the currently selected beatmap or mapset to or from a collection.\n- Press `2` or click `Delete...` to delete the \\[1\\] currently selected beatmapset, \\[2\\] delete the currently selected beatmap, or \\[3\\] delete **all VISIBLE beatmaps**.\n - Note that deleted beatmaps are moved to the Recycle Bin.\n- Press `3` or click `Remove from Unplayed` to mark an unplayed beatmap as played (that is, change its box colour from pink to orange).\n- Press `4` or click `Clear local scores` to delete all records of the scores you have achieved in this beatmap.\n- Press `5` or click `Edit` to open the selected beatmap in osu!'s Editor.\n- Press `6` or `Esc` or click `Close` to return to the Song Selection Screen.\n\nClick on **your user panel** to access the **User Options Menu**.\n\nClick the **[osu!cookie](/wiki/Glossary#cookie)** to **start playing the selected beatmap**.\n\n## Results screen\n\n![](img/results-osu.jpg \"Accuracy in osu!\")\n\nThis is the results screen shown after you have successfully passed the beatmap. You can access your online results by scrolling down or pressing the obvious button.\n\n**Note:** The results screen may change depending on the used skin.\n\nBelow are the results screens of the other game modes.\n\n![](img/results-taiko.jpg \"Accuracy in osu!taiko\")\n\n![](img/results-mania.jpg \"Accuracy in osu!mania\")\n\n![](img/results-catch.jpg \"Accuracy in osu!catch\")\n\n### Online Leaderboard\n\n![](img/extended-results-screen.jpg \"An example of an osu!online score\")\n\nThis is your online leaderboard. You can go here by scrolling down from the results screen. Your Local Scoreboard will show your name and the score as usual.\n\n1. Your player bar. It shows your [PP](/wiki/Performance_Points), Global Rank, Total Score, Overall [Accuracy](/wiki/Accuracy), and level bar.\n2. `Save replay to Replays folder`: You can watch the replay later either by opening it from a local leaderboard, or by going to `Replays` directory and double clicking it.\n3. `Add as online favourite`: Include the beatmap into your list of favourites, which is located on your osu! profile page under the \"Beatmaps\" section.\n4. Local Leaderboard: All your results are stored on your computer. To see them, navigate to the [song selection screen](#song-selection-screen), then select `Local Rankings` from the drop-down menu on the left.\n5. `Beatmap Ranking` section. Available only for maps with online leaderboards ([qualified](/wiki/Beatmap/Category#qualified), [ranked](/wiki/Beatmap/Category#ranked), or [loved](/wiki/Beatmap/Category#loved)). You also need to be online to see this section.\n 1. `Overall`: Your position on the map's leaderboard, where you compete against players that used [mods](/wiki/Game_modifier), even if you didn't use any yourself.\n 2. `Accuracy`: How [precisely](/wiki/Accuracy) did you play the beatmap. Will only be counted when your old score is surpassed.\n 3. `Max Combo`: Your longest combo on the map you played.\n 4. `Ranked Score`: Your [best result](/wiki/Score#ranked-score) on the beatmap.\n 5. `Total Score`: Not taken into account, since it does not affect your position in online rankings.\n 6. `Performance`: The amount of [unweighted PP](/wiki/Performance_points#why-didnt-i-gain-the-full-amount-of-pp-from-a-map-i-played) you would receive for the play.\n6. `Overall Ranking` section. It's available only for beatmaps with online leaderboards. You also need to be online to see this section.\n 1. `Overall`: Your global ranking in the world.\n 2. `Accuracy`: Your average [accuracy](/wiki/Accuracy#accuracy) over all beatmaps you have played.\n 3. `Max Combo`: The longest combo over all beatmaps you have played.\n 4. [`Ranked Score`](/wiki/Score#ranked-score): The number of points earned from all ranked beatmaps that you have ever played, with every map being counted exactly once.\n 5. [`Total Score`](/wiki/Score#total-score): Same as ranked score, but it takes into account all beatmaps available on the osu! website, and also underplayed or failed beatmaps. This counts towards your level.\n 6. `Perfomance`: Displays your total amount of Performance Points, and also how many PP the submitted play was worth.\n7. Information about the beatmap with its playcount and pass rate.\n8. Beatmap rating. Use your personal discretion based on whether you enjoy the beatmap or not. Best left alone if you can't decide.\n9. Click here to return to the song selection screen.\n\n![](img/medal-unlock.jpg \"Unlocking a medal\")\n\nAbove is what it looks like to receive a medal.\n", }; } } From 8dd3f11d28db64bb85256c4901f5ff8c26e47108 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:19:10 +0900 Subject: [PATCH 058/165] Tidy up struct and previous object handling --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 188 +++++++++++---------- 1 file changed, 97 insertions(+), 91 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d06e807500..5214020a84 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -52,35 +52,32 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - var prevObjectInfo = new HitObjectInfo + var prevObjectInfo = new RandomObjectInfo { - StartPosUnchanged = hitObjects[0].Position, - EndPosUnchanged = hitObjects[0].EndPosition, - StartPosChanged = hitObjects[0].Position, - EndPosChanged = hitObjects[0].EndPosition + PositionOriginal = hitObjects[0].Position, + EndPositionOriginal = hitObjects[0].EndPosition, + PositionRandomised = hitObjects[0].Position, + EndPositionRandomised = hitObjects[0].EndPosition }; float rateOfChangeMultiplier = 0; for (int i = 0; i < hitObjects.Count; i++) { - var h = hitObjects[i]; + var hitObject = hitObjects[i]; - var currentObjectInfo = new HitObjectInfo - { - StartPosUnchanged = h.Position, - EndPosUnchanged = h.EndPosition, - StartPosChanged = Vector2.Zero, - EndPosChanged = Vector2.Zero - }; + var currentObjectInfo = new RandomObjectInfo(hitObject); + + if (i == 0) + prevObjectInfo = currentObjectInfo; // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPosUnchanged, currentObjectInfo.StartPosUnchanged); + var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - switch (h) + switch (hitObject) { case HitCircle circle: getObjectInfo( @@ -90,15 +87,15 @@ namespace osu.Game.Rulesets.Osu.Mods ref currentObjectInfo ); - circle.Position = currentObjectInfo.StartPosChanged; - currentObjectInfo.EndPosChanged = currentObjectInfo.StartPosChanged; + circle.Position = currentObjectInfo.PositionRandomised; + currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; break; case Slider slider: - currentObjectInfo.EndPosUnchanged = slider.EndPosition; + currentObjectInfo.EndPositionOriginal = slider.EndPosition; - currentObjectInfo.EndPosUnchanged = slider.TailCircle.Position; + currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; getObjectInfo( rateOfChangeMultiplier, @@ -107,12 +104,12 @@ namespace osu.Game.Rulesets.Osu.Mods ref currentObjectInfo ); - slider.Position = currentObjectInfo.StartPosChanged; - currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + slider.Position = currentObjectInfo.PositionRandomised; + currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); - var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.StartPosUnchanged); + var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); foreach (var tick in slider.NestedHitObjects.OfType()) tick.Position = Vector2.Add(tick.Position, sliderShift); @@ -128,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object ///
/// Final position of the hit object - private void getObjectInfo(float rateOfChangeMultiplier, HitObjectInfo prevObjectInfo, float distanceToPrev, ref HitObjectInfo currentObjectInfo) + private void getObjectInfo(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -145,10 +142,10 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPosChanged, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.EndPosChanged, posRelativeToPrev); + var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -162,10 +159,10 @@ namespace osu.Game.Rulesets.Osu.Mods else if (position.Y > OsuPlayfield.BASE_SIZE.Y) position.Y = OsuPlayfield.BASE_SIZE.Y; - currentObjectInfo.StartPosChanged = position; + currentObjectInfo.PositionRandomised = position; } - private void moveSliderIntoPlayfield(ref Slider slider, ref HitObjectInfo currentObjectInfo) + private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) { foreach (var controlPoint in slider.Path.ControlPoints) { @@ -185,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Mods slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); } - currentObjectInfo.EndPosChanged = slider.TailCircle.Position; + currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; } /// @@ -266,89 +263,98 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - private struct HitObjectInfo + private struct RandomObjectInfo { internal float AngleRad { get; set; } - internal Vector2 StartPosUnchanged { get; set; } - internal Vector2 EndPosUnchanged { get; set; } - internal Vector2 StartPosChanged { get; set; } - internal Vector2 EndPosChanged { get; set; } - } - } - public class OsuModRandomSettingsControl : SettingsItem - { - protected override Drawable CreateControl() => new SeedControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 5 } - }; + internal Vector2 PositionOriginal { get; set; } + internal Vector2 PositionRandomised { get; set; } - private sealed class SeedControl : CompositeDrawable, IHasCurrentValue - { - private readonly BindableWithCurrent current = new BindableWithCurrent(); + internal Vector2 EndPositionOriginal { get; set; } + internal Vector2 EndPositionRandomised { get; set; } - public Bindable Current + public RandomObjectInfo(OsuHitObject hitObject) { - get => current; - set - { - current.Current = value; - seedNumberBox.Text = value.Value.ToString(); - } + PositionRandomised = PositionOriginal = hitObject.Position; + EndPositionRandomised = EndPositionOriginal = hitObject.EndPosition; + AngleRad = 0; } + } - private readonly OsuNumberBox seedNumberBox; - - public SeedControl() + public class OsuModRandomSettingsControl : SettingsItem + { + protected override Drawable CreateControl() => new SeedControl { - AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = 5 } + }; - InternalChildren = new[] + private sealed class SeedControl : CompositeDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current { - new GridContainer + get => current; + set { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - ColumnDimensions = new[] + current.Current = value; + seedNumberBox.Text = value.Value.ToString(); + } + } + + private readonly OsuNumberBox seedNumberBox; + + public SeedControl() + { + AutoSizeAxes = Axes.Y; + + InternalChildren = new[] + { + new GridContainer { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 2), - new Dimension(GridSizeMode.Relative, 0.25f) - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize) - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] { - seedNumberBox = new OsuNumberBox + new Dimension(), + new Dimension(GridSizeMode.Absolute, 2), + new Dimension(GridSizeMode.Relative, 0.25f) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.X, - CommitOnFocusLost = true + seedNumberBox = new OsuNumberBox + { + RelativeSizeAxes = Axes.X, + CommitOnFocusLost = true + } } } } - } - }; + }; - seedNumberBox.Current.BindValueChanged(e => + seedNumberBox.Current.BindValueChanged(e => + { + int? value = null; + + if (int.TryParse(e.NewValue, out var intVal)) + value = intVal; + + current.Value = value; + }); + } + + protected override void Update() { - int? value = null; - - if (int.TryParse(e.NewValue, out var intVal)) - value = intVal; - - current.Value = value; - }); - } - - protected override void Update() - { - if (current.Value == null) - seedNumberBox.Text = current.Current.Value.ToString(); + if (current.Value == null) + seedNumberBox.Text = current.Current.Value.ToString(); + } } } } From 88d7bc195dea687d508e6667b8dfd91c1162d1e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:24:56 +0900 Subject: [PATCH 059/165] Split out and clean up playfield sizing references --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 5214020a84..bbae746f67 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -29,10 +29,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "It never gets boring!"; public override bool Ranked => false; - // The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle. + // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn - private const byte border_distance_x = 192; - private const byte border_distance_y = 144; + private const float playfield_edge_ratio = 0.375f; + + private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio; + private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio; + + private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); + + private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] public Bindable Seed { get; } = new Bindable @@ -130,8 +136,7 @@ namespace osu.Game.Rulesets.Osu.Mods // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. - var maxDistance = OsuPlayfield.BASE_SIZE.LengthFast; - var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / maxDistance; + var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; if (currentObjectInfo.AngleRad < 0) @@ -145,6 +150,7 @@ namespace osu.Game.Rulesets.Osu.Mods posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, @@ -192,9 +198,8 @@ namespace osu.Game.Rulesets.Osu.Mods private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev) { var relativeRotationDistance = 0f; - var playfieldMiddle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2); - if (prevPosChanged.X < playfieldMiddle.X) + if (prevPosChanged.X < playfield_middle.X) { relativeRotationDistance = Math.Max( (border_distance_x - prevPosChanged.X) / border_distance_x, @@ -209,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - if (prevPosChanged.Y < playfieldMiddle.Y) + if (prevPosChanged.Y < playfield_middle.Y) { relativeRotationDistance = Math.Max( (border_distance_y - prevPosChanged.Y) / border_distance_y, @@ -224,11 +229,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - return rotateVectorTowardsVector( - posRelativeToPrev, - Vector2.Subtract(playfieldMiddle, prevPosChanged), - relativeRotationDistance / 2 - ); + return rotateVectorTowardsVector(posRelativeToPrev, playfield_middle - prevPosChanged, relativeRotationDistance / 2); } /// From a92ded8a2fc8e9e947d1258777000d5252aa81cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:28:07 +0900 Subject: [PATCH 060/165] Apply renaming and general code clean-up --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index bbae746f67..61559c06b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case HitCircle circle: - getObjectInfo( + applyRandomisation( rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; - getObjectInfo( + applyRandomisation( rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void getObjectInfo(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object @@ -155,15 +155,8 @@ namespace osu.Game.Rulesets.Osu.Mods // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. - if (position.X < 0) - position.X = 0; - else if (position.X > OsuPlayfield.BASE_SIZE.X) - position.X = OsuPlayfield.BASE_SIZE.X; - - if (position.Y < 0) - position.Y = 0; - else if (position.Y > OsuPlayfield.BASE_SIZE.Y) - position.Y = OsuPlayfield.BASE_SIZE.Y; + position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); + position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); currentObjectInfo.PositionRandomised = position; } From 53b5341bb98d7427b53edccbaed14bdd5250fbe5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 May 2021 14:33:07 +0900 Subject: [PATCH 061/165] Simplify application logic --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 52 +++++++--------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 61559c06b9..cc7732372f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -83,36 +83,24 @@ namespace osu.Game.Rulesets.Osu.Mods var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); + if (hitObject is Spinner) + continue; + + applyRandomisation( + rateOfChangeMultiplier, + prevObjectInfo, + distanceToPrev, + ref currentObjectInfo + ); + + hitObject.Position = currentObjectInfo.PositionRandomised; + + // update end position as it may have changed as a result of the position update. + currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; + switch (hitObject) { - case HitCircle circle: - applyRandomisation( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); - - circle.Position = currentObjectInfo.PositionRandomised; - currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; - - break; - case Slider slider: - currentObjectInfo.EndPositionOriginal = slider.EndPosition; - - currentObjectInfo.EndPositionOriginal = slider.TailCircle.Position; - - applyRandomisation( - rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - ref currentObjectInfo - ); - - slider.Position = currentObjectInfo.PositionRandomised; - currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; - moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); @@ -239,15 +227,9 @@ namespace osu.Game.Rulesets.Osu.Mods var diff = destAngleRad - initialAngleRad; - while (diff < -Math.PI) - { - diff += 2 * Math.PI; - } + while (diff < -Math.PI) diff += 2 * Math.PI; - while (diff > Math.PI) - { - diff -= 2 * Math.PI; - } + while (diff > Math.PI) diff -= 2 * Math.PI; var finalAngleRad = initialAngleRad + relativeDistance * diff; From 098d8c213178827f6b759ec9841cbb6a8cb8549b Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Mon, 24 May 2021 15:13:31 +0200 Subject: [PATCH 062/165] Add complete randomisation for first hit object and hit objects after spinners --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 33 ++++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index cc7732372f..ad2f4585f6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -58,35 +58,32 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - var prevObjectInfo = new RandomObjectInfo - { - PositionOriginal = hitObjects[0].Position, - EndPositionOriginal = hitObjects[0].EndPosition, - PositionRandomised = hitObjects[0].Position, - EndPositionRandomised = hitObjects[0].EndPosition - }; + RandomObjectInfo? prevObjectInfo = null; float rateOfChangeMultiplier = 0; for (int i = 0; i < hitObjects.Count; i++) { + var distanceToPrev = 0f; + var hitObject = hitObjects[i]; var currentObjectInfo = new RandomObjectInfo(hitObject); - if (i == 0) - prevObjectInfo = currentObjectInfo; + if (i > 0 && hitObjects[i - 1] is Spinner) + prevObjectInfo = null; + else if (prevObjectInfo != null) + distanceToPrev = Vector2.Distance(((RandomObjectInfo)prevObjectInfo).EndPositionOriginal, currentObjectInfo.PositionOriginal); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; - var distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - if (hitObject is Spinner) continue; applyRandomisation( + rng, rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, @@ -119,8 +116,20 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObjectInfo, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(Random rng, float rateOfChangeMultiplier, RandomObjectInfo? prevObjectInfoNullable, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) { + if (prevObjectInfoNullable == null) + { + var playfieldSize = OsuPlayfield.BASE_SIZE; + + currentObjectInfo.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); + currentObjectInfo.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); + + return; + } + + var prevObjectInfo = (RandomObjectInfo)prevObjectInfoNullable; + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. From 4c8f19af69cd09f0db9b9ad79931d90294f3f3b0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 14:47:39 +0700 Subject: [PATCH 063/165] load empty page when fail --- osu.Game/Overlays/WikiOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index fa8ba66bcd..df93c35500 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -47,6 +47,7 @@ namespace osu.Game.Overlays Loading.Show(); request.Success += response => Schedule(() => onSuccess(response)); + request.Failure += _ => Schedule(() => LoadDisplay(Empty())); api.PerformAsync(request); } From a3c78674a12022506f8cd42d2407e7dfe8ad176a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:09:24 +0900 Subject: [PATCH 064/165] Add new interface for autoplay mods --- .../Mods/EmptyFreeformModAutoplay.cs | 3 +-- .../Mods/PippidonModAutoplay.cs | 3 +-- .../Mods/EmptyScrollingModAutoplay.cs | 3 +-- .../Mods/PippidonModAutoplay.cs | 3 +-- osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs | 3 +-- osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs | 3 +-- osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs | 3 +-- osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs | 3 +-- osu.Game/Rulesets/Mods/ICreateReplay.cs | 14 ++++++++++++++ osu.Game/Rulesets/Mods/ModAutoplay.cs | 13 +------------ 10 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ICreateReplay.cs diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs index d5c1e9bd15..f705009d18 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Mods/EmptyFreeformModAutoplay.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.EmptyFreeform.Objects; using osu.Game.Rulesets.EmptyFreeform.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -11,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.EmptyFreeform.Mods { - public class EmptyFreeformModAutoplay : ModAutoplay + public class EmptyFreeformModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs index 8ea334c99c..4565c97d1a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Pippidon.Mods { - public class PippidonModAutoplay : ModAutoplay + public class PippidonModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs index 6dad1ff43b..431994e098 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/Mods/EmptyScrollingModAutoplay.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.EmptyScrolling.Objects; using osu.Game.Rulesets.EmptyScrolling.Replays; using osu.Game.Scoring; using osu.Game.Users; @@ -11,7 +10,7 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.EmptyScrolling.Mods { - public class EmptyScrollingModAutoplay : ModAutoplay + public class EmptyScrollingModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs index 8ea334c99c..4565c97d1a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Mods/PippidonModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Pippidon.Objects; using osu.Game.Rulesets.Pippidon.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Pippidon.Mods { - public class PippidonModAutoplay : ModAutoplay + public class PippidonModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs index e1eceea606..f1b51e51d0 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -11,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModAutoplay : ModAutoplay + public class CatchModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs index 105d88129c..6ae854e7f3 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -12,7 +11,7 @@ using osu.Game.Users; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModAutoplay : ModAutoplay + public class ManiaModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs index 3b1f271d41..652da7123e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutoplay.cs @@ -6,14 +6,13 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAutoplay : ModAutoplay + public class OsuModAutoplay : ModAutoplay { public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray(); diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs index 64e59b64d0..31d9abf8b2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModAutoplay.cs @@ -4,14 +4,13 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Scoring; using osu.Game.Users; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModAutoplay : ModAutoplay + public class TaikoModAutoplay : ModAutoplay { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { diff --git a/osu.Game/Rulesets/Mods/ICreateReplay.cs b/osu.Game/Rulesets/Mods/ICreateReplay.cs new file mode 100644 index 0000000000..098bd8799a --- /dev/null +++ b/osu.Game/Rulesets/Mods/ICreateReplay.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + public interface ICreateReplay + { + public Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods); + } +} diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index d6e1d46b06..b84b5671e1 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -7,22 +7,11 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Replays; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModAutoplay : ModAutoplay, IApplicableToDrawableRuleset - where T : HitObject - { - public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - drawableRuleset.SetReplayScore(CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods)); - } - } - - public abstract class ModAutoplay : Mod, IApplicableFailOverride + public abstract class ModAutoplay : Mod, IApplicableFailOverride, ICreateReplay { public override string Name => "Autoplay"; public override string Acronym => "AT"; From c2b938a29f5fef339a3adeb9ab62fa647a06dc12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:09:37 +0900 Subject: [PATCH 065/165] Remove autoplay consideration from `Player` --- osu.Game/Screens/Play/Player.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 39f9e2d388..b818dbea08 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -161,9 +161,7 @@ namespace osu.Game.Screens.Play if (!LoadedBeatmapSuccessfully) return; - // replays should never be recorded or played back when autoplay is enabled - if (!Mods.Value.Any(m => m is ModAutoplay)) - PrepareReplay(); + PrepareReplay(); gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true); } From 7f9318d97658d1dcbce49162c1fa3d7d9bdde4db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:36:47 +0900 Subject: [PATCH 066/165] Expose `GameplayBeatmap` to derived `Player` classes --- osu.Game/Screens/Play/Player.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b818dbea08..8e9c2fadcd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -145,7 +145,7 @@ namespace osu.Game.Screens.Play Configuration = configuration ?? new PlayerConfiguration(); } - private GameplayBeatmap gameplayBeatmap; + protected GameplayBeatmap GameplayBeatmap { get; private set; } private ScreenSuspensionHandler screenSuspension; @@ -221,10 +221,10 @@ namespace osu.Game.Screens.Play InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime); - AddInternal(gameplayBeatmap = new GameplayBeatmap(playableBeatmap)); + AddInternal(GameplayBeatmap = new GameplayBeatmap(playableBeatmap)); AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer)); - dependencies.CacheAs(gameplayBeatmap); + dependencies.CacheAs(GameplayBeatmap); var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); @@ -282,7 +282,7 @@ namespace osu.Game.Screens.Play { HealthProcessor.ApplyResult(r); ScoreProcessor.ApplyResult(r); - gameplayBeatmap.ApplyResult(r); + GameplayBeatmap.ApplyResult(r); }; DrawableRuleset.RevertResult += r => @@ -946,7 +946,7 @@ namespace osu.Game.Screens.Play using (var stream = new MemoryStream()) { - new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream); + new LegacyScoreEncoder(score, GameplayBeatmap.PlayableBeatmap).Encode(stream); replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); } From 7c89dbcd3502e68639476f9d99ab92e63205c870 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 May 2021 18:37:04 +0900 Subject: [PATCH 067/165] Externalise autoplay generation --- osu.Game/Screens/Play/ReplayPlayer.cs | 21 ++++++++++++++++-- osu.Game/Screens/Select/PlaySongSelect.cs | 26 +++++++++++++++-------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index e23cc22929..07c3d197da 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -1,9 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -11,15 +15,28 @@ namespace osu.Game.Screens.Play { public class ReplayPlayer : Player, IKeyBindingHandler { - protected readonly Score Score; + protected Score Score { get; private set; } + + private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; public ReplayPlayer(Score score, PlayerConfiguration configuration = null) + : this((_, __) => score, configuration) + { + } + + public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) : base(configuration) { - Score = score; + this.createScore = createScore; + } + + [BackgroundDependencyLoader] + private void load() + { + Score = createScore(GameplayBeatmap, Mods.Value); } protected override void PrepareReplay() diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index dfb4b59060..357222c109 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Select public class PlaySongSelect : SongSelect { private bool removeAutoModOnResume; - private OsuScreen player; + private OsuScreen playerLoader; [Resolved(CanBeNull = true)] private NotificationOverlay notifications { get; set; } @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select { base.OnResuming(last); - player = null; + playerLoader = null; if (removeAutoModOnResume) { @@ -79,14 +79,14 @@ namespace osu.Game.Screens.Select protected override bool OnStart() { - if (player != null) return false; + if (playerLoader != null) return false; // Ctrl+Enter should start map with autoplay enabled. if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) { - var autoplayMod = getAutoplayMod(); + var autoInstance = getAutoplayMod(); - if (autoplayMod == null) + if (autoInstance == null) { notifications?.Post(new SimpleNotification { @@ -97,18 +97,26 @@ namespace osu.Game.Screens.Select var mods = Mods.Value; - if (mods.All(m => m.GetType() != autoplayMod.GetType())) + if (mods.All(m => m.GetType() != autoInstance.GetType())) { - Mods.Value = mods.Append(autoplayMod).ToArray(); + Mods.Value = mods.Append(autoInstance).ToArray(); removeAutoModOnResume = true; } } SampleConfirm?.Play(); - this.Push(player = new PlayerLoader(() => new SoloPlayer())); - + this.Push(playerLoader = new PlayerLoader(createPlayer)); return true; + + Player createPlayer() + { + var autoplayMod = Mods.Value.OfType().FirstOrDefault(); + if (autoplayMod != null) + return new ReplayPlayer((beatmap, mods) => autoplayMod.CreateReplayScore(beatmap, mods)); + + return new SoloPlayer(); + } } } } From bdbd64c88d6a9cace3862fcdd45ccb6109d38cda Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 25 May 2021 21:32:18 +0200 Subject: [PATCH 068/165] Fix sliders being partly outside of the playfield in some cases --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 68 ++++++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index ad2f4585f6..98488fef8c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -98,13 +98,9 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case Slider slider: + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); - var sliderShift = Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal); - - foreach (var tick in slider.NestedHitObjects.OfType()) - tick.Position = Vector2.Add(tick.Position, sliderShift); - break; } @@ -158,27 +154,61 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.PositionRandomised = position; } + /// + /// Moves the and all necessary nested s into the if they aren't already. + /// private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) { - foreach (var controlPoint in slider.Path.ControlPoints) + var oldPos = new Vector2(slider.Position.X, slider.Position.Y); + + // Min. distances from the slider's position to the playfield border + var minMargin = new MarginPadding(0); + + foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle)) { - // Position of controlPoint relative to slider.Position - var pos = controlPoint.Position.Value; + if (!(hitObject is OsuHitObject osuHitObject)) + continue; - var playfieldSize = OsuPlayfield.BASE_SIZE; + var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position); - if (pos.X + slider.Position.X < 0) - slider.Position = new Vector2(-pos.X, slider.Position.Y); - else if (pos.X + slider.Position.X > playfieldSize.X) - slider.Position = new Vector2(playfieldSize.X - pos.X, slider.Position.Y); - - if (pos.Y + slider.Position.Y < 0) - slider.Position = new Vector2(slider.Position.X, -pos.Y); - else if (pos.Y + slider.Position.Y > playfieldSize.Y) - slider.Position = new Vector2(slider.Position.X, playfieldSize.Y - pos.Y); + minMargin.Left = Math.Max(minMargin.Left, -relativePos.X); + minMargin.Right = Math.Max(minMargin.Right, relativePos.X); + minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y); + minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y); } - currentObjectInfo.EndPositionRandomised = slider.TailCircle.Position; + if (slider.Position.X < minMargin.Left) + slider.Position = new Vector2(minMargin.Left, slider.Position.Y); + else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X) + slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y); + + if (slider.Position.Y < minMargin.Top) + slider.Position = new Vector2(slider.Position.X, minMargin.Top); + else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y) + slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom); + + currentObjectInfo.PositionRandomised = slider.Position; + currentObjectInfo.EndPositionRandomised = slider.EndPosition; + + var shift = Vector2.Subtract(slider.Position, oldPos); + + shiftNestedObjects(slider, shift); + } + + /// + /// Shifts all nested s and s by the specified shift. + /// + /// whose nested s and s should be shifted + /// The the 's nested s and s should be shifted by + private void shiftNestedObjects(Slider slider, Vector2 shift) + { + foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat)) + { + if (!(hitObject is OsuHitObject osuHitObject)) + continue; + + osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift); + } } /// From c85d5513bedb30f407594555b6bdaba3d10a2864 Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Tue, 25 May 2021 21:42:26 +0200 Subject: [PATCH 069/165] Remove redundant parameter and unused setters --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 98488fef8c..0f6b6d1afa 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Mods var oldPos = new Vector2(slider.Position.X, slider.Position.Y); // Min. distances from the slider's position to the playfield border - var minMargin = new MarginPadding(0); + var minMargin = new MarginPadding(); foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle)) { @@ -282,10 +282,10 @@ namespace osu.Game.Rulesets.Osu.Mods { internal float AngleRad { get; set; } - internal Vector2 PositionOriginal { get; set; } + internal Vector2 PositionOriginal { get; } internal Vector2 PositionRandomised { get; set; } - internal Vector2 EndPositionOriginal { get; set; } + internal Vector2 EndPositionOriginal { get; } internal Vector2 EndPositionRandomised { get; set; } public RandomObjectInfo(OsuHitObject hitObject) From ba8544f614570a565531fbcb04637bcacc66eb9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 14:55:44 +0900 Subject: [PATCH 070/165] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b3842a528d..f7ad06f5ca 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e3331cd365..a9152b8cb8 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 e35b1b5c42..a0894bc86a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From c5ff05209602584c388c8263b65395a0b8f1d22f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:31:25 +0900 Subject: [PATCH 071/165] Change `internal` to `public` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 0f6b6d1afa..b81e9fe3c5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -280,13 +280,13 @@ namespace osu.Game.Rulesets.Osu.Mods private struct RandomObjectInfo { - internal float AngleRad { get; set; } + public float AngleRad { get; set; } - internal Vector2 PositionOriginal { get; } - internal Vector2 PositionRandomised { get; set; } + public Vector2 PositionOriginal { get; } + public Vector2 PositionRandomised { get; set; } - internal Vector2 EndPositionOriginal { get; } - internal Vector2 EndPositionRandomised { get; set; } + public Vector2 EndPositionOriginal { get; } + public Vector2 EndPositionRandomised { get; set; } public RandomObjectInfo(OsuHitObject hitObject) { From 6181b1ac92302f5c8bae6f93ff2091da5cf81bde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:36:14 +0900 Subject: [PATCH 072/165] Simplify previous object handling by using a class instead of struct --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index b81e9fe3c5..d1d1d24f1b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods var rng = new Random((int)Seed.Value); - RandomObjectInfo? prevObjectInfo = null; + RandomObjectInfo prevObjectInfo = null; float rateOfChangeMultiplier = 0; @@ -70,24 +70,25 @@ namespace osu.Game.Rulesets.Osu.Mods var currentObjectInfo = new RandomObjectInfo(hitObject); - if (i > 0 && hitObjects[i - 1] is Spinner) - prevObjectInfo = null; - else if (prevObjectInfo != null) - distanceToPrev = Vector2.Distance(((RandomObjectInfo)prevObjectInfo).EndPositionOriginal, currentObjectInfo.PositionOriginal); + if (prevObjectInfo != null) + distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; if (hitObject is Spinner) + { + prevObjectInfo = null; continue; + } applyRandomisation( rng, rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, - ref currentObjectInfo + currentObjectInfo ); hitObject.Position = currentObjectInfo.PositionRandomised; @@ -99,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Mods { case Slider slider: shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); - moveSliderIntoPlayfield(ref slider, ref currentObjectInfo); + moveSliderIntoPlayfield(slider, currentObjectInfo); break; } @@ -112,9 +113,9 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object /// /// Final position of the hit object - private void applyRandomisation(Random rng, float rateOfChangeMultiplier, RandomObjectInfo? prevObjectInfoNullable, float distanceToPrev, ref RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObject, float distanceToPrev, RandomObjectInfo currentObjectInfo) { - if (prevObjectInfoNullable == null) + if (prevObject == null) { var playfieldSize = OsuPlayfield.BASE_SIZE; @@ -124,14 +125,12 @@ namespace osu.Game.Rulesets.Osu.Mods return; } - var prevObjectInfo = (RandomObjectInfo)prevObjectInfoNullable; - // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; - currentObjectInfo.AngleRad = (float)randomAngleRad + prevObjectInfo.AngleRad; + currentObjectInfo.AngleRad = (float)randomAngleRad + prevObject.AngleRad; if (currentObjectInfo.AngleRad < 0) currentObjectInfo.AngleRad += 2 * (float)Math.PI; @@ -140,11 +139,11 @@ namespace osu.Game.Rulesets.Osu.Mods distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(prevObject.EndPositionRandomised, posRelativeToPrev); currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObjectInfo.EndPositionRandomised, posRelativeToPrev); + var position = Vector2.Add(prevObject.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. @@ -157,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Moves the and all necessary nested s into the if they aren't already. /// - private void moveSliderIntoPlayfield(ref Slider slider, ref RandomObjectInfo currentObjectInfo) + private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { var oldPos = new Vector2(slider.Position.X, slider.Position.Y); @@ -278,7 +277,7 @@ namespace osu.Game.Rulesets.Osu.Mods ); } - private struct RandomObjectInfo + private class RandomObjectInfo { public float AngleRad { get; set; } From 6ca9b37c28279e05d599903047c2be4484a28ce6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:37:30 +0900 Subject: [PATCH 073/165] Make random generator a field to avoid passing around internally --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index d1d1d24f1b..c53c262ffb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; + private Random rng; + [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(OsuModRandomSettingsControl))] public Bindable Seed { get; } = new Bindable { @@ -56,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods Seed.Value ??= RNG.Next(); - var rng = new Random((int)Seed.Value); + rng = new Random((int)Seed.Value); RandomObjectInfo prevObjectInfo = null; @@ -83,9 +85,7 @@ namespace osu.Game.Rulesets.Osu.Mods continue; } - applyRandomisation( - rng, - rateOfChangeMultiplier, + applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, distanceToPrev, currentObjectInfo From ad3e4287cd24da058cff136293b3b7913b68730c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:44:05 +0900 Subject: [PATCH 074/165] Move `distanceToPrev` inside randomisation function --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 39 +++++++++------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index c53c262ffb..1c59569517 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -66,15 +66,10 @@ namespace osu.Game.Rulesets.Osu.Mods for (int i = 0; i < hitObjects.Count; i++) { - var distanceToPrev = 0f; - var hitObject = hitObjects[i]; var currentObjectInfo = new RandomObjectInfo(hitObject); - if (prevObjectInfo != null) - distanceToPrev = Vector2.Distance(prevObjectInfo.EndPositionOriginal, currentObjectInfo.PositionOriginal); - // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1; @@ -85,11 +80,7 @@ namespace osu.Game.Rulesets.Osu.Mods continue; } - applyRandomisation(rateOfChangeMultiplier, - prevObjectInfo, - distanceToPrev, - currentObjectInfo - ); + applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, currentObjectInfo); hitObject.Position = currentObjectInfo.PositionRandomised; @@ -113,44 +104,46 @@ namespace osu.Game.Rulesets.Osu.Mods /// Returns the final position of the hit object ///
/// Final position of the hit object - private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo prevObject, float distanceToPrev, RandomObjectInfo currentObjectInfo) + private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current) { - if (prevObject == null) + if (previous == null) { var playfieldSize = OsuPlayfield.BASE_SIZE; - currentObjectInfo.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); - currentObjectInfo.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); + current.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI); + current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y); return; } + float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal); + // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object) // is proportional to the distance between the last and the current hit object // to allow jumps and prevent too sharp turns during streams. var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal; - currentObjectInfo.AngleRad = (float)randomAngleRad + prevObject.AngleRad; - if (currentObjectInfo.AngleRad < 0) - currentObjectInfo.AngleRad += 2 * (float)Math.PI; + current.AngleRad = (float)randomAngleRad + previous.AngleRad; + if (current.AngleRad < 0) + current.AngleRad += 2 * (float)Math.PI; var posRelativeToPrev = new Vector2( - distanceToPrev * (float)Math.Cos(currentObjectInfo.AngleRad), - distanceToPrev * (float)Math.Sin(currentObjectInfo.AngleRad) + distanceToPrev * (float)Math.Cos(current.AngleRad), + distanceToPrev * (float)Math.Sin(current.AngleRad) ); - posRelativeToPrev = getRotatedVector(prevObject.EndPositionRandomised, posRelativeToPrev); + posRelativeToPrev = getRotatedVector(previous.EndPositionRandomised, posRelativeToPrev); - currentObjectInfo.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); + current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X); - var position = Vector2.Add(prevObject.EndPositionRandomised, posRelativeToPrev); + var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev); // Move hit objects back into the playfield if they are outside of it, // which would sometimes happen during big jumps otherwise. position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X); position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y); - currentObjectInfo.PositionRandomised = position; + current.PositionRandomised = position; } /// From d6c4be207b05530b39fc520cb03a28f7c082680b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:44:44 +0900 Subject: [PATCH 075/165] Simplify naming --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1c59569517..a9aeba99fa 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Mods rng = new Random((int)Seed.Value); - RandomObjectInfo prevObjectInfo = null; + RandomObjectInfo previous = null; float rateOfChangeMultiplier = 0; @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var hitObject = hitObjects[i]; - var currentObjectInfo = new RandomObjectInfo(hitObject); + var current = new RandomObjectInfo(hitObject); // rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams if (i % 3 == 0) @@ -76,27 +76,27 @@ namespace osu.Game.Rulesets.Osu.Mods if (hitObject is Spinner) { - prevObjectInfo = null; + previous = null; continue; } - applyRandomisation(rateOfChangeMultiplier, prevObjectInfo, currentObjectInfo); + applyRandomisation(rateOfChangeMultiplier, previous, current); - hitObject.Position = currentObjectInfo.PositionRandomised; + hitObject.Position = current.PositionRandomised; // update end position as it may have changed as a result of the position update. - currentObjectInfo.EndPositionRandomised = currentObjectInfo.PositionRandomised; + current.EndPositionRandomised = current.PositionRandomised; switch (hitObject) { case Slider slider: - shiftNestedObjects(slider, Vector2.Subtract(slider.Position, currentObjectInfo.PositionOriginal)); - moveSliderIntoPlayfield(slider, currentObjectInfo); + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); + moveSliderIntoPlayfield(slider, current); break; } - prevObjectInfo = currentObjectInfo; + previous = current; } } From a08a4aa9111448432fb708fdc17a8014f66a1c59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 16:48:16 +0900 Subject: [PATCH 076/165] Move second call to `shiftNestedObjects` to a more understandable location --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index a9aeba99fa..58ace92905 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -91,8 +91,13 @@ namespace osu.Game.Rulesets.Osu.Mods { case Slider slider: shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); + + var oldPos = new Vector2(slider.Position.X, slider.Position.Y); + moveSliderIntoPlayfield(slider, current); + shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos)); + break; } @@ -151,8 +156,6 @@ namespace osu.Game.Rulesets.Osu.Mods /// private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo) { - var oldPos = new Vector2(slider.Position.X, slider.Position.Y); - // Min. distances from the slider's position to the playfield border var minMargin = new MarginPadding(); @@ -181,10 +184,6 @@ namespace osu.Game.Rulesets.Osu.Mods currentObjectInfo.PositionRandomised = slider.Position; currentObjectInfo.EndPositionRandomised = slider.EndPosition; - - var shift = Vector2.Subtract(slider.Position, oldPos); - - shiftNestedObjects(slider, shift); } /// From 7546611c1421fb83eae255dd6544d78023e89ee4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:32:49 +0700 Subject: [PATCH 077/165] remove unused cached --- osu.Game/Overlays/WikiOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index df93c35500..040e608574 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -20,7 +20,6 @@ namespace osu.Game.Overlays private readonly Bindable path = new Bindable(index_path); - [Cached] private readonly Bindable wikiData = new Bindable(); [Resolved] From 905364b5fefc07cd309b1e8b77327ad6d9a7e115 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 19:34:34 +0700 Subject: [PATCH 078/165] add url as argument for link action external --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 1041758b0c..c57fc732be 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -173,7 +173,7 @@ namespace osu.Game.Online.Chat } } - return new LinkDetails(LinkAction.External, null); + return new LinkDetails(LinkAction.External, url); case "osu": // every internal link also needs some kind of argument From 22a5af750e6be5cffea6f6a706f59581f39c9f1e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Wed, 26 May 2021 20:25:48 +0700 Subject: [PATCH 079/165] fix test link external --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index b80da928c8..c0fc19356e 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tests.Chat [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc/def", "https://dev.ppy.sh/beatmapsets/abc/def")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc", "https://dev.ppy.sh/beatmapsets/abc")] public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link) { MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; From d5de5ae6406f85fee9217c8c1649d93e0547e5dc Mon Sep 17 00:00:00 2001 From: Pasi4K5 Date: Wed, 26 May 2021 20:50:31 +0200 Subject: [PATCH 080/165] Add comments explaining the usage of `shiftNestedObjects()` --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 58ace92905..7b28675511 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -90,12 +90,15 @@ namespace osu.Game.Rulesets.Osu.Mods switch (hitObject) { case Slider slider: + // Shift nested objects the same distance as the slider got shifted in the randomisation process + // so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal)); var oldPos = new Vector2(slider.Position.X, slider.Position.Y); moveSliderIntoPlayfield(slider, current); + // Shift them again to move them to their final position after the slider got moved into the playfield shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos)); break; From b349ff8693096103d14a94411b8f25cb647044d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:04 +0900 Subject: [PATCH 081/165] Revert "Add temporary accounting for tests with null files" This reverts commit e52c0a34f82a6b93c740b4c2d3d068bc3c52196a. --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2e2bc96c21..f70d29861f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -293,11 +293,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; - // files may be null in some tests. - if (beatmapInfo.BeatmapSet?.Files == null) - return DefaultBeatmap; - - if (beatmapInfo.BeatmapSet.Files.Count == 0) + if (beatmapInfo.BeatmapSet.Files == null) { var info = beatmapInfo; beatmapInfo = QueryBeatmap(b => b.ID == info.ID); From 41733af0ed8eefd3e7206115680ad45588d2e652 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:05 +0900 Subject: [PATCH 082/165] Revert "Revert "Make `BeatmapSetInfo.Files` non-nullable"" This reverts commit 9c4f39e9681644d84b60828ee8ce613b6d81a89f. --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++++------------ osu.Game/Beatmaps/BeatmapSetInfo.cs | 10 ++++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f70d29861f..f459657a0e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; @@ -245,8 +244,6 @@ namespace osu.Game.Beatmaps { var setInfo = info.BeatmapSet; - Debug.Assert(setInfo.Files != null); - using (var stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -287,20 +284,21 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { - if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; - - if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; - if (beatmapInfo.BeatmapSet.Files == null) + if (beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + + // if there are no files, presume the full beatmap info has not yet been fetched from the database. + if (beatmapInfo.BeatmapSet.Files.Count == 0) { - var info = beatmapInfo; - beatmapInfo = QueryBeatmap(b => b.ID == info.ID); + int lookupId = beatmapInfo.ID; + beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } - if (beatmapInfo == null) - return DefaultBeatmap; + if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) + return previous; lock (workingCache) { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 1ce42535a0..3b1ff4ced0 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Testing; using osu.Game.Database; @@ -31,6 +32,9 @@ namespace osu.Game.Beatmaps public List Beatmaps { get; set; } + [NotNull] + public List Files { get; set; } = new List(); + [NotMapped] public BeatmapSetOnlineInfo OnlineInfo { get; set; } @@ -57,16 +61,14 @@ namespace osu.Game.Beatmaps public string Hash { get; set; } - public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; /// /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. /// The path returned is relative to the user file storage. /// /// The name of the file to get the storage path of. - public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; - - public List Files { get; set; } + public string GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; public override string ToString() => Metadata?.ToString() ?? base.ToString(); From 581a86b91a231e5ed12d49bc1267d840fb58232b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 14:33:06 +0900 Subject: [PATCH 083/165] Revert "Revert "Fix editor tests failing due to empty files being specified"" This reverts commit 1af684c4b2cb41e45f473e90285f5ac5cc192d12. --- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 + osu.Game/Beatmaps/BeatmapManager.cs | 4 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 - osu.Game/Tests/Visual/EditorTestScene.cs | 43 ++++++++++++++++++- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 7584c74c71..dd5e01adbb 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual.Editing protected override bool EditorComponentsReady => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true; + protected override bool IsolateSavingFromDatabase => false; + [Resolved] private BeatmapManager beatmapManager { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f459657a0e..18fbd1f5c2 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -240,7 +240,7 @@ namespace osu.Game.Beatmaps /// The to save the content against. The file referenced by will be replaced. /// The content to write. /// The beatmap content to write, null if to be omitted. - public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) { var setInfo = info.BeatmapSet; @@ -282,7 +282,7 @@ namespace osu.Game.Beatmaps /// The beatmap to lookup. /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) { if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index fa6dc5647d..2717146c99 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.Ruleset = ruleset; BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; - BeatmapInfo.BeatmapSet.Files = new List(); BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index a9ee8e2668..de7fdd0cf8 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -4,11 +4,18 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.IO.Archives; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual { @@ -20,10 +27,20 @@ namespace osu.Game.Tests.Visual protected EditorClock EditorClock { get; private set; } + /// + /// Whether any saves performed by the editor should be isolate (and not persist) to the underlying . + /// + protected virtual bool IsolateSavingFromDatabase => true; + [BackgroundDependencyLoader] - private void load() + private void load(GameHost host, AudioManager audio, RulesetStore rulesets) { - Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + var working = CreateWorkingBeatmap(Ruleset.Value); + + Beatmap.Value = working; + + if (IsolateSavingFromDatabase) + Dependencies.CacheAs(new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default, working)); } protected virtual bool EditorComponentsReady => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true @@ -65,5 +82,27 @@ namespace osu.Game.Tests.Visual public new bool HasUnsavedChanges => base.HasUnsavedChanges; } + + private class TestBeatmapManager : BeatmapManager + { + private readonly WorkingBeatmap testBeatmap; + + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap) + : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, false) + { + this.testBeatmap = testBeatmap; + } + + protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) + => string.Empty; + + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + => testBeatmap; + + public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) + { + // don't actually care about saving for this context. + } + } } } From 631d217f788a7d6808acd4337bdf018d3320539e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 15:42:35 +0900 Subject: [PATCH 084/165] Remove no longer necessary conditional access --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 2 +- osu.Game/Storyboards/Storyboard.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 4ea582ca4a..d746ff5ae5 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -29,7 +29,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader(true)] private void load(IBindable beatmap, TextureStore textureStore) { - var path = beatmap.Value.BeatmapSetInfo?.Files?.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var path = beatmap.Value.BeatmapSetInfo?.Files.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (path == null) return; diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 3486c1d66a..38e0e4e38c 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -91,7 +91,7 @@ namespace osu.Game.Storyboards public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) { Drawable drawable = null; - var storyboardPath = BeatmapInfo.BeatmapSet?.Files?.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var storyboardPath = BeatmapInfo.BeatmapSet?.Files.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (storyboardPath != null) drawable = new Sprite { Texture = textureStore.Get(storyboardPath) }; From 60b781701fb03854a19c6f3b9f7ebaac5249234b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 May 2021 14:20:03 +0300 Subject: [PATCH 085/165] Rewrite catch combo counter hide logic --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 05580f6d06..802de55b7b 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -29,20 +29,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy switch (targetComponent.Target) { case SkinnableTarget.MainHUDComponents: - if (!providesComboCounter) - break; + var components = Source.GetDrawableComponent(component) as SkinnableTargetComponentsContainer; - if (Source.GetDrawableComponent(component) is SkinnableTargetComponentsContainer components) + if (providesComboCounter && components != null) { // catch may provide its own combo counter; hide the default. // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) legacyComboCounter.ContentVisible = false; - - return components; } - break; + return components; } } From d12e93bfc67598007192899b440726396b8e202e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 00:02:55 +0300 Subject: [PATCH 086/165] Add skin traget resetting on setup/teardown steps --- .../Tests/Visual/LegacySkinPlayerTestScene.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index 907a37f4b9..f78d05c6e5 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,9 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -25,6 +27,29 @@ namespace osu.Game.Tests.Visual legacySkinSource = new SkinProvidingContainer(LegacySkin); } + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + addResetTargetsStep(); + } + + [TearDownSteps] + public override void TearDownSteps() + { + addResetTargetsStep(); + base.TearDownSteps(); + } + + private void addResetTargetsStep() + { + AddStep("reset targets", () => this.ChildrenOfType().ForEach(t => + { + LegacySkin.ResetDrawableTarget(t); + t.Reload(); + })); + } + public class SkinProvidingPlayer : TestPlayer { [Cached(typeof(ISkinSource))] From 3fbd4e276d632b2763858a8efa031a0d43b57d71 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 00:07:29 +0300 Subject: [PATCH 087/165] Add simple xmldoc --- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index ee00c71b0f..112b36616c 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -47,6 +47,13 @@ namespace osu.Game.Screens.Play.HUD private readonly Container counterContainer; + /// + /// Changes the visibility state of the combo counter internally without affecting its . + /// + /// + /// This is temporarily done for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// without potentially affecting the user's selected skin. + /// public bool ContentVisible { set => counterContainer.Alpha = value ? 1 : 0; From 113f90e92e341f826926a1a3d2f4dcd9387df952 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Mon, 31 May 2021 10:49:16 +0700 Subject: [PATCH 088/165] add back schedule is CurrentPath setter --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 4e671cca6d..1c5fd99ce3 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Wiki.Markdown { public string CurrentPath { - set => DocumentUrl = $"{DocumentUrl}wiki/{value}"; + set => Schedule(() => DocumentUrl = $"{DocumentUrl}wiki/{value}"); } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) From fb111e23d86b02c5c8cc42c843fa706b5fe67481 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 07:24:59 +0300 Subject: [PATCH 089/165] Remove "temporarily" --- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 112b36616c..6c2752569d 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play.HUD /// Changes the visibility state of the combo counter internally without affecting its . /// /// - /// This is temporarily done for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// This is used for rulesets that provide their own combo counter and don't want the HUD one to be visible, /// without potentially affecting the user's selected skin. /// public bool ContentVisible From 06bd696cc21eb4e20667875093f790468cc474e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 14:11:57 +0900 Subject: [PATCH 090/165] Remove `previous` consumption logic in `GetWorkingBeatmap` This should not be required since the introduction of `workingCache`, which does the same thing in a more global way. --- osu.Game/Beatmaps/BeatmapManager.cs | 6 +----- osu.Game/Overlays/MusicController.cs | 4 ++-- osu.Game/Screens/Select/SongSelect.cs | 7 ++++--- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index b2cd0c9392..5a26f5b66c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -280,9 +280,8 @@ namespace osu.Game.Beatmaps /// Retrieve a instance for the provided ///
/// The beatmap to lookup. - /// The currently loaded . Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches /// A instance correlating to the provided . - public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) { if (beatmapInfo?.BeatmapSet == null) return DefaultBeatmap; @@ -294,9 +293,6 @@ namespace osu.Game.Beatmaps beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } - if (beatmapInfo.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID) - return previous; - lock (workingCache) { var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 3a9a6261ba..a15f80ca21 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -252,7 +252,7 @@ namespace osu.Game.Overlays if (playable != null) { - changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value)); + changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First())); restartTrack(); return PreviousTrackResult.Previous; } @@ -283,7 +283,7 @@ namespace osu.Game.Overlays if (playable != null) { - changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value)); + changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First())); restartTrack(); return true; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 74e10037ab..270addc8e6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -505,12 +505,13 @@ namespace osu.Game.Screens.Select { Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\""); - WorkingBeatmap previous = Beatmap.Value; - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous); + int? lastSetID = Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID; + + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); if (beatmap != null) { - if (beatmap.BeatmapSetInfoID == previous?.BeatmapInfo.BeatmapSetInfoID) + if (beatmap.BeatmapSetInfoID == lastSetID) sampleChangeDifficulty.Play(); else sampleChangeBeatmap.Play(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index de7fdd0cf8..ca331088ce 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) => string.Empty; - public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) => testBeatmap; public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null) From e25cca6fbe0271976e1a711ab0037c6493b412f0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:37:28 +0900 Subject: [PATCH 091/165] Update drawable lifetime on entry lifetime change --- .../Pooling/PoolableDrawableWithLifetime.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 64e1ac16bd..66d4421cdb 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -3,6 +3,7 @@ #nullable enable +using System; using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; @@ -26,17 +27,18 @@ namespace osu.Game.Rulesets.Objects.Pooling /// protected bool HasEntryApplied { get; private set; } - // Drawable's lifetime gets out of sync with entry's lifetime if entry's lifetime is modified. - // We cannot delegate getter to `Entry.LifetimeStart` because it is incompatible with `LifetimeManagementContainer` due to how lifetime change is detected. public override double LifetimeStart { get => base.LifetimeStart; set { - base.LifetimeStart = value; + if (Entry == null && LifetimeStart != value) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry != null) - Entry.LifetimeStart = value; + if (Entry == null) return; + + Entry.LifetimeStart = value; + base.LifetimeStart = Entry.LifetimeStart; } } @@ -45,10 +47,13 @@ namespace osu.Game.Rulesets.Objects.Pooling get => base.LifetimeEnd; set { - base.LifetimeEnd = value; + if (Entry == null && LifetimeEnd != value) + throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry != null) - Entry.LifetimeEnd = value; + if (Entry == null) return; + + Entry.LifetimeEnd = value; + base.LifetimeEnd = Entry.LifetimeEnd; } } @@ -79,9 +84,8 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; - - base.LifetimeStart = entry.LifetimeStart; - base.LifetimeEnd = entry.LifetimeEnd; + entry.LifetimeChanged += entryLifetimeChanged; + setLifetimeFromEntry(); OnApply(entry); @@ -117,11 +121,23 @@ namespace osu.Game.Rulesets.Objects.Pooling OnFree(Entry); + Entry.LifetimeChanged -= entryLifetimeChanged; Entry = null; - base.LifetimeStart = double.MinValue; - base.LifetimeEnd = double.MaxValue; + setLifetimeFromEntry(); HasEntryApplied = false; } + + private void entryLifetimeChanged(LifetimeEntry entry) + { + Debug.Assert(entry == Entry); + setLifetimeFromEntry(); + } + + private void setLifetimeFromEntry() + { + base.LifetimeStart = Entry?.LifetimeStart ?? double.MinValue; + base.LifetimeEnd = Entry?.LifetimeEnd ?? double.MaxValue; + } } } From b7afea37c4f8facce6a43e362371c2470110572c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:37:51 +0900 Subject: [PATCH 092/165] Test drawable lifetime change on entry lifetime change --- .../Gameplay/TestSceneDrawableHitObject.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 3de2dc72bb..5dfc9a2786 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -92,6 +92,35 @@ namespace osu.Game.Tests.Gameplay AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY); } + [Test] + public void TestDrawableLifetimeUpdateOnEntryLifetimeChange() + { + TestDrawableHitObject dho = null; + TestLifetimeEntry entry = null; + AddStep("Create DHO", () => + { + dho = new TestDrawableHitObject(null); + dho.Apply(entry = new TestLifetimeEntry(new HitObject())); + Child = dho; + }); + + AddStep("Set entry lifetime", () => + { + entry.LifetimeStart = 777; + entry.LifetimeEnd = 888; + }); + AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == 777 && dho.LifetimeEnd == 888); + + AddStep("KeepAlive = true", () => entry.KeepAlive = true); + AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == double.MinValue && dho.LifetimeEnd == double.MaxValue); + + AddStep("Modify start time", () => entry.HitObject.StartTime = 100); + AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + + AddStep("KeepAlive = false", () => entry.KeepAlive = false); + AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + } + private class TestDrawableHitObject : DrawableHitObject { public const double INITIAL_LIFETIME_OFFSET = 100; From 771f3c48c08874c7cf2b8564c153c01ef2561619 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 14:48:52 +0900 Subject: [PATCH 093/165] Add failing test showing lifetime not recomputed with pooled objects --- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 9931ee4a45..75a5eec6f7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay assertChildPosition(5); } + [TestCase("pooled")] + [TestCase("non-pooled")] + public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled) + { + var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject()); + beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); + createTest(beatmap); + + assertDead(3); + + AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range); + assertPosition(3, 1); + } + [Test] public void TestRelativeBeatLengthScaleSingleTimingPoint() { From 4e186b0cf55360f9b5f9442c49844e42b0cd50e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 31 May 2021 09:24:26 +0300 Subject: [PATCH 094/165] `ContentVisible` -> `HiddenByRulesetImplementation` --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 2 +- osu.Game/Screens/Play/HUD/LegacyComboCounter.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 802de55b7b..8c9e602cd4 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy // catch may provide its own combo counter; hide the default. // todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed. foreach (var legacyComboCounter in components.OfType()) - legacyComboCounter.ContentVisible = false; + legacyComboCounter.HiddenByRulesetImplementation = false; } return components; diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 6c2752569d..1737634e31 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -48,13 +48,13 @@ namespace osu.Game.Screens.Play.HUD private readonly Container counterContainer; /// - /// Changes the visibility state of the combo counter internally without affecting its . + /// Hides the combo counter internally without affecting its . /// /// - /// This is used for rulesets that provide their own combo counter and don't want the HUD one to be visible, + /// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible, /// without potentially affecting the user's selected skin. /// - public bool ContentVisible + public bool HiddenByRulesetImplementation { set => counterContainer.Alpha = value ? 1 : 0; } From 56a0a24cbaf0d09df98929f2ff7576d85a00971a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 15:33:28 +0900 Subject: [PATCH 095/165] Make SetInitialLifetime public --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index f654fa91cf..5ad0d6246e 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Objects HitObject = hitObject; startTimeBindable.BindTo(HitObject.StartTimeBindable); - startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true); + startTimeBindable.BindValueChanged(_ => SetInitialLifetime(), true); // Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied. // This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime(). - HitObject.DefaultsApplied += _ => setInitialLifetime(); + HitObject.DefaultsApplied += _ => SetInitialLifetime(); } // The lifetime, as set by the hitobject. @@ -94,6 +94,6 @@ namespace osu.Game.Rulesets.Objects /// /// Set using . /// - private void setInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; + public void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } } From 3cedc0824dab6614bfe191aa6836d02882021c64 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:02:33 +0900 Subject: [PATCH 096/165] Don't add nested hit objects to the sets Only top-level hit objects are checked for layout computation caching. Also, lifetime of nested hit objects are not managed by the HitObjectContainer. --- .../Scrolling/ScrollingHitObjectContainer.cs | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index a9eaf3da68..46308ac38e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly HashSet toComputeLifetime = new HashSet(); /// - /// A set containing all which have an up-to-date layout. + /// A set of top-level s which have an up-to-date layout. /// private readonly HashSet layoutComputed = new HashSet(); @@ -150,29 +150,18 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - protected override void OnAdd(DrawableHitObject drawableHitObject) => onAddRecursive(drawableHitObject); - - protected override void OnRemove(DrawableHitObject drawableHitObject) => onRemoveRecursive(drawableHitObject); - - private void onAddRecursive(DrawableHitObject hitObject) + protected override void OnAdd(DrawableHitObject drawableHitObject) { - invalidateHitObject(hitObject); - - hitObject.DefaultsApplied += invalidateHitObject; - - foreach (var nested in hitObject.NestedHitObjects) - onAddRecursive(nested); + invalidateHitObject(drawableHitObject); + drawableHitObject.DefaultsApplied += invalidateHitObject; } - private void onRemoveRecursive(DrawableHitObject hitObject) + protected override void OnRemove(DrawableHitObject drawableHitObject) { - toComputeLifetime.Remove(hitObject); - layoutComputed.Remove(hitObject); + toComputeLifetime.Remove(drawableHitObject); + layoutComputed.Remove(drawableHitObject); - hitObject.DefaultsApplied -= invalidateHitObject; - - foreach (var nested in hitObject.NestedHitObjects) - onRemoveRecursive(nested); + drawableHitObject.DefaultsApplied -= invalidateHitObject; } /// From 742c5b442be0186966de975e168c13905ba3b9db Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:24:13 +0900 Subject: [PATCH 097/165] Remove delay of lifetime update The scheduling is no longer necessary because `OnAdd` is changed to not invoked immediately for non-pooled DHOs. --- .../Scrolling/ScrollingHitObjectContainer.cs | 62 ++++++------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 46308ac38e..aed12653d9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -17,11 +17,6 @@ namespace osu.Game.Rulesets.UI.Scrolling private readonly IBindable timeRange = new BindableDouble(); private readonly IBindable direction = new Bindable(); - /// - /// Hit objects which require lifetime computation in the next update call. - /// - private readonly HashSet toComputeLifetime = new HashSet(); - /// /// A set of top-level s which have an up-to-date layout. /// @@ -54,7 +49,6 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Clear(); - toComputeLifetime.Clear(); layoutComputed.Clear(); } @@ -158,20 +152,14 @@ namespace osu.Game.Rulesets.UI.Scrolling protected override void OnRemove(DrawableHitObject drawableHitObject) { - toComputeLifetime.Remove(drawableHitObject); layoutComputed.Remove(drawableHitObject); drawableHitObject.DefaultsApplied -= invalidateHitObject; } - /// - /// Make this lifetime and layout computed in next update. - /// private void invalidateHitObject(DrawableHitObject hitObject) { - // Lifetime computation is delayed until next update because - // when the hit object is not pooled this container is not loaded here and `scrollLength` cannot be computed. - toComputeLifetime.Add(hitObject); + hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); layoutComputed.Remove(hitObject); } @@ -181,39 +169,29 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Update(); - if (!layoutCache.IsValid) + if (layoutCache.IsValid) return; + + foreach (var hitObject in Objects) { - toComputeLifetime.Clear(); - - foreach (var hitObject in Objects) - { - if (hitObject.HitObject != null) - toComputeLifetime.Add(hitObject); - } - - layoutComputed.Clear(); - - scrollingInfo.Algorithm.Reset(); - - switch (direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - - default: - scrollLength = DrawSize.X; - break; - } - - layoutCache.Validate(); + if (hitObject.HitObject != null) + invalidateHitObject(hitObject); } - foreach (var hitObject in toComputeLifetime) - hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject); + scrollingInfo.Algorithm.Reset(); - toComputeLifetime.Clear(); + switch (direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + scrollLength = DrawSize.Y; + break; + + default: + scrollLength = DrawSize.X; + break; + } + + layoutCache.Validate(); } protected override void UpdateAfterChildrenLife() From 6d968467761d962051963ddb215f9dcb161102e9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:28:02 +0900 Subject: [PATCH 098/165] Remove `scrollLength` caching field It was not clear when the field is updated. --- .../Scrolling/ScrollingHitObjectContainer.cs | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index aed12653d9..b174632498 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.UI.Scrolling flipPositionIfRequired(ref position); - return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, scrollLength); } /// @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// public Vector2 ScreenSpacePositionAtTime(double time) { - var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength()); + var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, scrollLength); flipPositionIfRequired(ref pos); @@ -100,16 +100,19 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private float getLength() + private float scrollLength { - switch (scrollingInfo.Direction.Value) + get { - case ScrollingDirection.Left: - case ScrollingDirection.Right: - return DrawWidth; + switch (scrollingInfo.Direction.Value) + { + case ScrollingDirection.Left: + case ScrollingDirection.Right: + return DrawWidth; - default: - return DrawHeight; + default: + return DrawHeight; + } } } @@ -163,8 +166,6 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Remove(hitObject); } - private float scrollLength; - protected override void Update() { base.Update(); @@ -179,18 +180,6 @@ namespace osu.Game.Rulesets.UI.Scrolling scrollingInfo.Algorithm.Reset(); - switch (direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - - default: - scrollLength = DrawSize.X; - break; - } - layoutCache.Validate(); } From 86d1225aad80e1ac2cd79e2b22719b1df895713b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 16:10:31 +0900 Subject: [PATCH 099/165] Reset lifetime to initial lifetime when layout is invalidated --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index a9eaf3da68..10d6815c0a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -195,15 +195,13 @@ namespace osu.Game.Rulesets.UI.Scrolling if (!layoutCache.IsValid) { toComputeLifetime.Clear(); - - foreach (var hitObject in Objects) - { - if (hitObject.HitObject != null) - toComputeLifetime.Add(hitObject); - } - layoutComputed.Clear(); + // Reset lifetime to the conservative estimation. + // If a drawable becomes alive by this lifetime, its lifetime will be updated to a more precise lifetime in the next update. + foreach (var entry in Entries) + entry.SetInitialLifetime(); + scrollingInfo.Algorithm.Reset(); switch (direction.Value) From 675fe3744692a8a525c8e2769051bbd5f3561fcd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:24:42 +0900 Subject: [PATCH 100/165] Change check order around to ensure re-fetches which return no results don't nullref --- osu.Game/Beatmaps/BeatmapManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5a26f5b66c..518ef2cc19 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -283,16 +283,16 @@ namespace osu.Game.Beatmaps /// A instance correlating to the provided . public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo) { - if (beatmapInfo?.BeatmapSet == null) - return DefaultBeatmap; - // if there are no files, presume the full beatmap info has not yet been fetched from the database. - if (beatmapInfo.BeatmapSet.Files.Count == 0) + if (beatmapInfo?.BeatmapSet?.Files.Count == 0) { int lookupId = beatmapInfo.ID; beatmapInfo = QueryBeatmap(b => b.ID == lookupId); } + if (beatmapInfo?.BeatmapSet == null) + return DefaultBeatmap; + lock (workingCache) { var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); From b16d10bd95af2d67c37adeeacfe6ca527abe52bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:37:32 +0900 Subject: [PATCH 101/165] Provide game-wide resources via `IStorageResourceProvider` --- .../TestSceneManiaHitObjectSamples.cs | 2 +- .../TestSceneTaikoHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs | 1 + .../TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 7 ++++--- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 2 +- .../Collections/TestSceneManageCollectionsDialog.cs | 2 +- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 2 +- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- .../Multiplayer/TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- .../Multiplayer/TestSceneMultiplayerSpectateButton.cs | 2 +- .../Visual/Multiplayer/TestScenePlaylistsSongSelect.cs | 2 +- .../Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs | 2 +- .../Visual/SongSelect/TestSceneFilterControl.cs | 2 +- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 5 ++++- osu.Game/IO/IStorageResourceProvider.cs | 5 +++++ osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Skinning/SkinManager.cs | 8 ++++++-- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 4 +++- osu.Game/Tests/Visual/OsuTestScene.cs | 5 +++++ osu.Game/Tests/Visual/SkinnableTestScene.cs | 1 + 25 files changed, 47 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs index 0d726e1a50..eedabd1adb 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests public class TestSceneManiaHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); - protected override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); + public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); /// /// Tests that when a normal sample bank is used, the normal hitsound will be looked up. diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 7089ea6619..1258784a14 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); - protected override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); + public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 64eaafbe75..afd4365c45 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Gameplay public class TestSceneHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - protected override IResourceStore Resources => TestResources.GetStore(); + public override IResourceStore Resources => TestResources.GetStore(); /// /// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin. diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index bbab9ae94d..aed28f5f84 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -219,6 +219,7 @@ namespace osu.Game.Tests.Gameplay public AudioManager AudioManager => Audio; public IResourceStore Files => null; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; #endregion diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 8dab570e30..42848ffc0c 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -44,7 +45,7 @@ namespace osu.Game.Tests.Online private void load(AudioManager audio, GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.CacheAs(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, host, Beatmap.Default)); + Dependencies.CacheAs(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, Resources, host, Beatmap.Default)); } [SetUp] @@ -160,8 +161,8 @@ namespace osu.Game.Tests.Online protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new TestDownloadRequest(set); - public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) - : base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, performOnlineLookups) + public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) + : base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, performOnlineLookups) { } diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f89988cd1a..1670d86545 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Background private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); Dependencies.Cache(new OsuConfigManager(LocalStorage)); manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index eca857f9e5..28218ea220 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Collections private void load(GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default)); + Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 960aad10c6..dfb78a235b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 424efb255b..c5a6723508 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); } [SetUp] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index faa5d9e6fc..5b059c06f5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps = new List(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index f611d5fecf..e8ebc0c426 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index dfb4306e67..929cd6ca80 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); Add(beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index e59b342176..d00404102c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); var beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = selectedItem } }; base.Content.Add(beatmapTracker); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index 7d83ba569d..d95a95ebe5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); var beatmaps = new List(); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 264004b6c3..a08a91314b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index c13bdf0955..a5b90e6655 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void load(GameHost host) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default)); + Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default)); beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait(); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 2eb6d3f80e..102e5ee425 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void load(GameHost host, AudioManager audio) { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); - Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, defaultBeatmap = Beatmap.Default)); Dependencies.Cache(music = new MusicController()); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 97f3b2954d..3f9e0048dd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory)); - dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), dependencies.Get(), Beatmap.Default)); + dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory)); beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0]; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e7f6bb3c3a..829327d37b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -73,6 +73,7 @@ namespace osu.Game.Beatmaps private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; private readonly AudioManager audioManager; + private readonly IResourceStore resources; private readonly LargeTextureStore largeTextureStore; private readonly ITrackStore trackStore; @@ -82,12 +83,13 @@ namespace osu.Game.Beatmaps [CanBeNull] private readonly BeatmapOnlineLookupQueue onlineLookupQueue; - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false) : base(storage, contextFactory, api, new BeatmapStore(contextFactory), host) { this.rulesets = rulesets; this.audioManager = audioManager; + this.resources = resources; this.host = host; DefaultBeatmap = defaultBeatmap; @@ -511,6 +513,7 @@ namespace osu.Game.Beatmaps ITrackStore IBeatmapResourceProvider.Tracks => trackStore; AudioManager IStorageResourceProvider.AudioManager => audioManager; IResourceStore IStorageResourceProvider.Files => Files.Store; + IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); #endregion diff --git a/osu.Game/IO/IStorageResourceProvider.cs b/osu.Game/IO/IStorageResourceProvider.cs index cbd1039807..e4c97e18fa 100644 --- a/osu.Game/IO/IStorageResourceProvider.cs +++ b/osu.Game/IO/IStorageResourceProvider.cs @@ -19,6 +19,11 @@ namespace osu.Game.IO /// IResourceStore Files { get; } + /// + /// Access game-wide resources. + /// + IResourceStore Resources { get; } + /// /// Create a texture loader store based on an underlying data store. /// diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 918f231a19..7935815f38 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -209,7 +209,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); + dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Resources, Audio)); dependencies.CacheAs(SkinManager); // needs to be done here rather than inside SkinManager to ensure thread safety of CurrentSkinInfo. @@ -242,7 +242,7 @@ namespace osu.Game // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 5793edda30..034b920c1a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -37,6 +37,8 @@ namespace osu.Game.Skinning private readonly GameHost host; + private readonly IResourceStore resources; + private readonly IResourceStore legacyDefaultResources; public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); @@ -48,13 +50,14 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, AudioManager audio, IResourceStore legacyDefaultResources) + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore resources, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { this.audio = audio; this.host = host; + this.resources = resources; - this.legacyDefaultResources = legacyDefaultResources; + legacyDefaultResources = new NamespacedResourceStore(resources, "Skins/Legacy"); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => @@ -216,6 +219,7 @@ namespace osu.Game.Skinning #region IResourceStorageProvider AudioManager IStorageResourceProvider.AudioManager => audio; + IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.Files => Files.Store; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 62814d4ed4..038ab334e8 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -28,7 +28,8 @@ namespace osu.Game.Tests.Beatmaps [HeadlessTest] public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider { - protected abstract IResourceStore Resources { get; } + protected abstract IResourceStore RulesetResources { get; } + protected LegacySkin Skin { get; private set; } [Resolved] @@ -127,6 +128,7 @@ namespace osu.Game.Tests.Beatmaps public AudioManager AudioManager => Audio; public IResourceStore Files => userSkinResourceStore; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; #endregion diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 198d22fedd..be9a015ab2 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -14,6 +14,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Timing; @@ -49,6 +50,8 @@ namespace osu.Game.Tests.Visual private Lazy contextFactory; + protected IResourceStore Resources; + protected IAPIProvider API { get @@ -81,6 +84,8 @@ namespace osu.Game.Tests.Visual if (!UseFreshStoragePerRun) isolatedHostStorage = (parent.Get() as HeadlessGameHost)?.Storage; + Resources = parent.Get().Resources; + contextFactory = new Lazy(() => { var factory = new DatabaseContextFactory(LocalStorage); diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 3d2c68c2ad..729c12187f 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -156,6 +156,7 @@ namespace osu.Game.Tests.Visual public AudioManager AudioManager => Audio; public IResourceStore Files => null; + public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); #endregion From 65709ec7d7853657128db2613dda60ad5c80dd05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:58:40 +0900 Subject: [PATCH 102/165] Move leagcy resource store construction local to `DefaultLegacySkin` --- osu.Game/Skinning/DefaultLegacySkin.cs | 8 ++++---- osu.Game/Skinning/SkinInfo.cs | 6 +----- osu.Game/Skinning/SkinManager.cs | 6 +----- osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs | 5 ++--- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 ++-- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index f30130b1fb..30192182f3 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -11,14 +11,14 @@ namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { - public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) - : this(Info, storage, resources) + public DefaultLegacySkin(IStorageResourceProvider resources) + : this(Info, resources) { } [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] - public DefaultLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources) - : base(skin, storage, resources, string.Empty) + public DefaultLegacySkin(SkinInfo skin, IStorageResourceProvider resources) + : base(skin, new NamespacedResourceStore(resources.Resources, "Skins/Legacy"), resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.AddComboColours( diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 55760876e3..e30bc16d8b 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Extensions.ObjectExtensions; -using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Extensions; @@ -28,16 +27,13 @@ namespace osu.Game.Skinning public string InstantiationInfo { get; set; } - public virtual Skin CreateInstance(IResourceStore legacyDefaultResources, IStorageResourceProvider resources) + public virtual Skin CreateInstance(IStorageResourceProvider resources) { var type = string.IsNullOrEmpty(InstantiationInfo) // handle the case of skins imported before InstantiationInfo was added. ? typeof(LegacySkin) : Type.GetType(InstantiationInfo).AsNonNull(); - if (type == typeof(DefaultLegacySkin)) - return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources); - return (Skin)Activator.CreateInstance(type, this, resources); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 034b920c1a..079c537066 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,8 +39,6 @@ namespace osu.Game.Skinning private readonly IResourceStore resources; - private readonly IResourceStore legacyDefaultResources; - public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; @@ -57,8 +55,6 @@ namespace osu.Game.Skinning this.host = host; this.resources = resources; - legacyDefaultResources = new NamespacedResourceStore(resources, "Skins/Legacy"); - CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { @@ -155,7 +151,7 @@ namespace osu.Game.Skinning /// /// The skin to lookup. /// A instance correlating to the provided . - public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this); + public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(this); /// /// Ensure that the current skin is in a state it can accept user modifications. diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs index c186525757..e3d7a21ab0 100644 --- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs +++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.IO.Stores; using osu.Game.Rulesets; using osu.Game.Skinning; @@ -17,9 +16,9 @@ namespace osu.Game.Tests.Visual protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource); [BackgroundDependencyLoader] - private void load(OsuGameBase game, SkinManager skins) + private void load(SkinManager skins) { - var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), skins); + var legacySkin = new DefaultLegacySkin(skins); legacySkinSource = new SkinProvidingContainer(legacySkin); } diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 729c12187f..e801412fc5 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -40,12 +40,12 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(AudioManager audio, SkinManager skinManager, OsuGameBase game) + private void load(AudioManager audio, SkinManager skinManager) { var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly); metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), this, true); - defaultSkin = new DefaultLegacySkin(new NamespacedResourceStore(game.Resources, "Skins/Legacy"), this); + defaultSkin = new DefaultLegacySkin(this); specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore(dllStore, "Resources/special_skin"), this, true); oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore(dllStore, "Resources/old_skin"), this, true); } From ebfc24a499eb56e17b8ee12f008b7b4526931dad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 18:55:47 +0900 Subject: [PATCH 103/165] Rename conflicting resources --- .../TestSceneManiaHitObjectSamples.cs | 2 +- .../TestSceneTaikoHitObjectSamples.cs | 2 +- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 +- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs index eedabd1adb..ea57e51d1c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectSamples.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests public class TestSceneManiaHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); - public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); + protected override IResourceStore RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples))); /// /// Tests that when a normal sample bank is used, the normal hitsound will be looked up. diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs index 1258784a14..221d715a35 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); - public override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); + protected override IResourceStore RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); [TestCase("taiko-normal-hitnormal")] [TestCase("normal-hitnormal")] diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index afd4365c45..fc420e22a1 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Gameplay public class TestSceneHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); - public override IResourceStore Resources => TestResources.GetStore(); + protected override IResourceStore RulesetResources => TestResources.GetStore(); /// /// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin. diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 038ab334e8..7ee6c519b7 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider { protected abstract IResourceStore RulesetResources { get; } - protected LegacySkin Skin { get; private set; } [Resolved] @@ -76,7 +75,7 @@ namespace osu.Game.Tests.Beatmaps AddStep($"load {filename}", () => { - using (var reader = new LineBufferedReader(Resources.GetStream($"Resources/SampleLookups/{filename}"))) + using (var reader = new LineBufferedReader(RulesetResources.GetStream($"Resources/SampleLookups/{filename}"))) currentTestBeatmap = Decoder.GetDecoder(reader).Decode(reader); // populate ruleset for beatmap converters that require it to be present. From e78391db7a298ec4e0d84516832dfe8c51e9f5a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 19:57:31 +0900 Subject: [PATCH 104/165] Fix usage of DI before it's ready in combo colour tests --- .../TestSceneLegacyBeatmapSkin.cs | 14 +++++++------- .../TestSceneLegacyBeatmapSkin.cs | 10 +++++----- .../Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs | 12 ++++++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index eea83ef7c1..28ff02cb1f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverride(useBeatmapSkin); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public void TestBeatmapHyperDashColours(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); ConfigureTest(useBeatmapSkin, true, true); AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR); AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false)] public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); ConfigureTest(useBeatmapSkin, false, true); AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR); AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index c26419b0e8..66f919f91f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverride(useBeatmapSkin); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false)] public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, false)] public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 051ede30b7..708963c760 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -21,8 +21,10 @@ namespace osu.Game.Tests.Beatmaps { protected readonly Bindable BeatmapSkins = new Bindable(); protected readonly Bindable BeatmapColours = new Bindable(); + protected ExposedPlayer TestPlayer; - protected WorkingBeatmap TestBeatmap; + + private WorkingBeatmap testBeatmap; public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours); @@ -34,10 +36,12 @@ namespace osu.Game.Tests.Beatmaps public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true); - protected virtual void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) + protected void PrepareBeatmap(Func createBeatmap) => AddStep("prepare beatmap", () => testBeatmap = createBeatmap()); + + protected void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) { configureSettings(useBeatmapSkin, useBeatmapColours); - AddStep($"load {(((CustomSkinWorkingBeatmap)TestBeatmap).HasColours ? "coloured " : "")} beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours)); + AddStep("load beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours)); AddUntilStep("wait for player load", () => TestPlayer.IsLoaded); } @@ -57,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps { ExposedPlayer player; - Beatmap.Value = TestBeatmap; + Beatmap.Value = testBeatmap; LoadScreen(player = CreateTestPlayer(userHasCustomColours)); From f60e9cb08588760c1355bc9b41a1fc012e308080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 31 May 2021 20:00:47 +0900 Subject: [PATCH 105/165] Remove weird override logic in `TestCase` methods --- .../TestSceneLegacyBeatmapSkin.cs | 20 +++++++++---------- .../TestSceneLegacyBeatmapSkin.cs | 20 +++++++++---------- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 10 ---------- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index 28ff02cb1f..bc3daca16f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -32,28 +32,28 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, true); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -61,10 +61,10 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -72,10 +72,10 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, true); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 66f919f91f..56307861f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -30,28 +30,28 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + ConfigureTest(useBeatmapSkin, true, userHasCustomColours); AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverride(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, true); AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true)); - base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + ConfigureTest(useBeatmapSkin, false, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, false); AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false)); - base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + ConfigureTest(useBeatmapSkin, useBeatmapColour, true); AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index 708963c760..0a7fb1483d 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -26,16 +26,6 @@ namespace osu.Game.Tests.Beatmaps private WorkingBeatmap testBeatmap; - public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours); - - public virtual void TestBeatmapComboColoursOverride(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, true); - - public virtual void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, false); - - public virtual void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, false); - - public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true); - protected void PrepareBeatmap(Func createBeatmap) => AddStep("prepare beatmap", () => testBeatmap = createBeatmap()); protected void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) From ff0494229524c7c5d984895f8500c31b8329185e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 20:56:25 +0900 Subject: [PATCH 106/165] Change timing of `HitObjectContainer.OnAdd`/`OnRemove` Now, `OnAdd` is always invoked after the DHO is added to the container. This change is required by `ScrollingHitObjectContainer` to compute origin adjusted lifetime when origin position is relative to the parent drawable size. --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index dcf350cbd4..83033b2dd5 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -122,18 +122,20 @@ namespace osu.Game.Rulesets.UI var entry = (HitObjectLifetimeEntry)lifetimeEntry; Debug.Assert(!aliveDrawableMap.ContainsKey(entry)); - bool isNonPooled = nonPooledDrawableMap.TryGetValue(entry, out var drawable); + bool isPooled = !nonPooledDrawableMap.TryGetValue(entry, out var drawable); drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); aliveDrawableMap[entry] = drawable; + + if (isPooled) + { + addDrawable(drawable); + HitObjectUsageBegan?.Invoke(entry.HitObject); + } + OnAdd(drawable); - - if (isNonPooled) return; - - addDrawable(drawable); - HitObjectUsageBegan?.Invoke(entry.HitObject); } private void entryBecameDead(LifetimeEntry lifetimeEntry) @@ -142,17 +144,18 @@ namespace osu.Game.Rulesets.UI Debug.Assert(aliveDrawableMap.ContainsKey(entry)); var drawable = aliveDrawableMap[entry]; - bool isNonPooled = nonPooledDrawableMap.ContainsKey(entry); + bool isPooled = !nonPooledDrawableMap.ContainsKey(entry); drawable.OnKilled(); aliveDrawableMap.Remove(entry); + + if (isPooled) + { + removeDrawable(drawable); + HitObjectUsageFinished?.Invoke(entry.HitObject); + } + OnRemove(drawable); - - if (isNonPooled) return; - - removeDrawable(drawable); - // The hit object is not freed when the DHO was not pooled. - HitObjectUsageFinished?.Invoke(entry.HitObject); } private void addDrawable(DrawableHitObject drawable) @@ -211,21 +214,16 @@ namespace osu.Game.Rulesets.UI #endregion /// - /// Invoked when a is added to this container. + /// Invoked after a is added to this container. /// - /// - /// This method is not invoked for nested s. - /// protected virtual void OnAdd(DrawableHitObject drawableHitObject) { + Debug.Assert(drawableHitObject.LoadState >= LoadState.Ready); } /// - /// Invoked when a is removed from this container. + /// Invoked after a is removed from this container. /// - /// - /// This method is not invoked for nested s. - /// protected virtual void OnRemove(DrawableHitObject drawableHitObject) { } From 86d1ba7ef29c16408c2726f822a28c1e5fb84ba5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 31 May 2021 23:17:26 +0900 Subject: [PATCH 107/165] Remove unused `IScrollingHitObject` interface --- .../Objects/Drawables/IScrollingHitObject.cs | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs diff --git a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs deleted file mode 100644 index 48fcfabc2f..0000000000 --- a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Framework.Graphics; - -namespace osu.Game.Rulesets.Objects.Drawables -{ - /// - /// An interface that exposes properties required for scrolling hit objects to be properly displayed. - /// - internal interface IScrollingHitObject : IDrawable - { - /// - /// Time offset before the hit object start time at which this becomes visible and the time offset - /// after the hit object's end time after which it expires. - /// - /// - /// This provides only a default life time range, however classes inheriting from should override - /// their life times if more tight control is desired. - /// - /// - BindableDouble LifetimeOffset { get; } - - /// - /// Axes which this will scroll through. - /// This is set by the container which this scrolls through. - /// - Axes ScrollingAxes { set; } - } -} From d5714e63b9611f2743130f5c689e5b95a6f4f53b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 13:17:30 +0900 Subject: [PATCH 108/165] Apply code styling suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Pooling/PoolableDrawableWithLifetime.cs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 66d4421cdb..5125243bfc 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart; + base.LifetimeStart = Entry.LifetimeStart = value; } } @@ -52,8 +51,7 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - Entry.LifetimeEnd = value; - base.LifetimeEnd = Entry.LifetimeEnd; + base.LifetimeEnd = Entry.LifetimeEnd = value; } } @@ -84,8 +82,8 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; - entry.LifetimeChanged += entryLifetimeChanged; - setLifetimeFromEntry(); + entry.LifetimeChanged += setLifetimeFromEntry; + setLifetimeFromEntry(entry); OnApply(entry); @@ -121,23 +119,19 @@ namespace osu.Game.Rulesets.Objects.Pooling OnFree(Entry); - Entry.LifetimeChanged -= entryLifetimeChanged; + Entry.LifetimeChanged -= setLifetimeFromEntry; Entry = null; - setLifetimeFromEntry(); + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; HasEntryApplied = false; } - private void entryLifetimeChanged(LifetimeEntry entry) + private void setLifetimeFromEntry(LifetimeEntry entry) { Debug.Assert(entry == Entry); - setLifetimeFromEntry(); - } - - private void setLifetimeFromEntry() - { - base.LifetimeStart = Entry?.LifetimeStart ?? double.MinValue; - base.LifetimeEnd = Entry?.LifetimeEnd ?? double.MaxValue; + base.LifetimeStart = entry.LifetimeStart; + base.LifetimeEnd = entry.LifetimeEnd; } } } From 1a05a5d2f02dca6d74be4c8af2a33e111379d8f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:50:04 +0900 Subject: [PATCH 109/165] Add test covering failure to resolve relative URLs --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index b80da928c8..6f9007d670 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -489,5 +489,14 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00"); Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); } + + [Test] + public void TestRelativeExternalLinks() + { + LinkDetails result = MessageFormatter.GetLinkDetails("/relative"); + + Assert.AreEqual(LinkAction.External, result.Action); + Assert.AreEqual("/relative", result.Argument); + } } } From 111bfd4d88abe484931b8789d36653832b005264 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:50:20 +0900 Subject: [PATCH 110/165] Fix relative URLs having a null argument after resolution to `LinkDetails` --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index b80720a0aa..54d7030457 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -217,7 +217,7 @@ namespace osu.Game.Online.Chat return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]); } - return new LinkDetails(LinkAction.External, null); + return new LinkDetails(LinkAction.External, url); } private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3) From 3ff97f787aaf9a72ffec5f271c81fe2abbf81eb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 13:32:52 +0900 Subject: [PATCH 111/165] Localise all URL generation for now to avoid weird `Schedule` logic --- .../Containers/Markdown/OsuMarkdownContainer.cs | 12 ------------ .../Overlays/Wiki/Markdown/WikiMarkdownContainer.cs | 7 ++++++- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 4 +++- osu.Game/Overlays/WikiOverlay.cs | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index ad11a9625e..6facf4e26c 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -6,13 +6,11 @@ using Markdig.Extensions.AutoIdentifiers; using Markdig.Extensions.Tables; using Markdig.Extensions.Yaml; using Markdig.Syntax; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API; namespace osu.Game.Graphics.Containers.Markdown { @@ -23,16 +21,6 @@ namespace osu.Game.Graphics.Containers.Markdown LineSpacing = 21; } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var api = parent.Get(); - - // needs to be set before the base BDL call executes to avoid invalidating any already populated markdown content. - DocumentUrl = api.WebsiteRootUrl; - - return base.CreateChildDependencies(parent); - } - protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) { switch (markdownObject) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 1c5fd99ce3..acaaa523a2 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -5,17 +5,22 @@ using System.Linq; using Markdig.Extensions.Yaml; using Markdig.Syntax; using Markdig.Syntax.Inlines; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown; +using osu.Game.Online.API; namespace osu.Game.Overlays.Wiki.Markdown { public class WikiMarkdownContainer : OsuMarkdownContainer { + [Resolved] + private IAPIProvider api { get; set; } + public string CurrentPath { - set => Schedule(() => DocumentUrl = $"{DocumentUrl}wiki/{value}"); + set => DocumentUrl = value; } protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index db213e4951..e1c00a955b 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers.Markdown; +using osu.Game.Online.API; using osu.Game.Overlays.Wiki.Markdown; using osuTK; using osuTK.Graphics; @@ -37,7 +38,7 @@ namespace osu.Game.Overlays.Wiki } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, IAPIProvider api) { Children = new Drawable[] { @@ -61,6 +62,7 @@ namespace osu.Game.Overlays.Wiki }, panelContainer = new WikiPanelMarkdownContainer(isFullWidth) { + CurrentPath = $@"{api.WebsiteRootUrl}/wiki/", Text = text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 040e608574..a87a592b56 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - CurrentPath = $"{path.Value}/", + CurrentPath = $@"{api.WebsiteRootUrl}/wiki/{path.Value}/", Text = response.Markdown, DocumentMargin = new MarginPadding(0), DocumentPadding = new MarginPadding From 6e4730652e6829a6b54014aebd6dc5a5ad5823d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:01:08 +0900 Subject: [PATCH 112/165] Push private methods down --- osu.Game/Overlays/WikiOverlay.cs | 92 ++++++++++++++++---------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index a87a592b56..8468a48bf5 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -36,6 +36,52 @@ namespace osu.Game.Overlays { } + public void ShowPage(string pagePath = index_path) + { + path.Value = pagePath.Trim('/'); + Show(); + } + + protected override WikiHeader CreateHeader() => new WikiHeader + { + ShowIndexPage = () => ShowPage(), + ShowParentPage = showParentPage, + }; + + protected override void LoadComplete() + { + base.LoadComplete(); + path.BindValueChanged(onPathChanged); + wikiData.BindTo(Header.WikiPageData); + } + + protected override void PopIn() + { + base.PopIn(); + + if (displayUpdateRequired) + { + path.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + displayUpdateRequired = true; + } + + protected void LoadDisplay(Drawable display) + { + ScrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + Child = loaded; + Loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + private void onPathChanged(ValueChangedEvent e) { cancellationToken?.Cancel(); @@ -92,52 +138,6 @@ namespace osu.Game.Overlays ShowPage(parentPath); } - public void ShowPage(string pagePath = index_path) - { - path.Value = pagePath.Trim('/'); - Show(); - } - - protected override WikiHeader CreateHeader() => new WikiHeader - { - ShowIndexPage = () => ShowPage(), - ShowParentPage = showParentPage, - }; - - protected override void LoadComplete() - { - base.LoadComplete(); - path.BindValueChanged(onPathChanged); - wikiData.BindTo(Header.WikiPageData); - } - - protected override void PopIn() - { - base.PopIn(); - - if (displayUpdateRequired) - { - path.TriggerChange(); - displayUpdateRequired = false; - } - } - - protected override void PopOutComplete() - { - base.PopOutComplete(); - displayUpdateRequired = true; - } - - protected void LoadDisplay(Drawable display) - { - ScrollFlow.ScrollToStart(); - LoadComponentAsync(display, loaded => - { - Child = loaded; - Loading.Hide(); - }, (cancellationToken = new CancellationTokenSource()).Token); - } - protected override void Dispose(bool isDisposing) { cancellationToken?.Cancel(); From 27b703952059d79c50f3563b04a85db77b73e74f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:02:32 +0900 Subject: [PATCH 113/165] Use constant --- osu.Game/Overlays/WikiOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 8468a48bf5..af7bc40f17 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays { public class WikiOverlay : OnlineOverlay { - private const string index_path = "Main_Page"; + private const string index_path = @"main_page"; private readonly Bindable path = new Bindable(index_path); @@ -101,7 +101,7 @@ namespace osu.Game.Overlays { wikiData.Value = response; - if (response.Layout == "main_page") + if (response.Layout == index_path) { LoadDisplay(new WikiMainPage { From a9f4bc6285dcc84bd9cf4582b6bc2b7557815908 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:09:35 +0900 Subject: [PATCH 114/165] Never return a null argument Enable nullable --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 13 +++++++++++-- osu.Game/Online/Chat/MessageFormatter.cs | 8 +++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index 6f9007d670..ecb37706b0 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tests.Chat [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")] [TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc/def", "https://dev.ppy.sh/beatmapsets/abc/def")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")] [TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")] - [TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")] + [TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc", "https://dev.ppy.sh/beatmapsets/abc")] public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link) { MessageFormatter.WebsiteRootUrl = "dev.ppy.sh"; @@ -490,6 +490,15 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20"); } + [Test] + public void TestAbsoluteExternalLinks() + { + LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com"); + + Assert.AreEqual(LinkAction.External, result.Action); + Assert.AreEqual("https://google.com", result.Argument); + } + [Test] public void TestRelativeExternalLinks() { diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 54d7030457..e08cb1b35f 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +#nullable enable + namespace osu.Game.Online.Chat { public static class MessageFormatter @@ -61,7 +63,7 @@ namespace osu.Game.Online.Chat private static string websiteRootUrl = "osu.ppy.sh"; - private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null) + private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[]? escapeChars = null) { int captureOffset = 0; @@ -170,12 +172,12 @@ namespace osu.Game.Online.Chat } } - return new LinkDetails(LinkAction.External, null); + break; case "osu": // every internal link also needs some kind of argument if (args.Length < 3) - return new LinkDetails(LinkAction.External, null); + break; LinkAction linkType; From 9221213fe527bdc4e33188a1a9a7642edd240ab4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:19:13 +0900 Subject: [PATCH 115/165] Fix potential nullref is beatmap load failed --- osu.Game/Screens/Play/ReplayPlayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 07c3d197da..f26675cc64 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { + if (!LoadedBeatmapSuccessfully) return; + Score = createScore(GameplayBeatmap, Mods.Value); } From cbf3ef5400bbc7ea2e75f532845175f99a002341 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:22:16 +0900 Subject: [PATCH 116/165] Create replay via the `ICreateReplay` interface instead of explicitly `ModAutoplay` --- osu.Game/Screens/Select/PlaySongSelect.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 357222c109..418cf23ce7 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -111,9 +111,9 @@ namespace osu.Game.Screens.Select Player createPlayer() { - var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - if (autoplayMod != null) - return new ReplayPlayer((beatmap, mods) => autoplayMod.CreateReplayScore(beatmap, mods)); + var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + if (replayGeneratingMod != null) + return new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateReplayScore(beatmap, mods)); return new SoloPlayer(); } From 86020adf6463efc2565a5ffb518270eb91ce5285 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:22:12 +0900 Subject: [PATCH 117/165] Revert invalid code transformation --- .../Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index 5125243bfc..b1515e2ac2 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; + // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). + Entry.LifetimeStart = value; base.LifetimeStart = Entry.LifetimeStart = value; } } @@ -51,7 +53,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null) return; - base.LifetimeEnd = Entry.LifetimeEnd = value; + Entry.LifetimeEnd = value; + base.LifetimeEnd = Entry.LifetimeEnd; } } From 977d44df87a22eeed43851a89b86525523c8d51c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:25:32 +0900 Subject: [PATCH 118/165] Add test catching lifetime change while KeepAlive is true --- osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index 5dfc9a2786..b5b3cec15d 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -117,8 +117,12 @@ namespace osu.Game.Tests.Gameplay AddStep("Modify start time", () => entry.HitObject.StartTime = 100); AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999); + AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue); + AddStep("KeepAlive = false", () => entry.KeepAlive = false); - AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); + AddAssert("Drawable lifetime is restored", () => + dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET && dho.LifetimeEnd == 999); } private class TestDrawableHitObject : DrawableHitObject From 6ef9b346e12e7b1f5f63177990947efb5c5eae8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:33:21 +0900 Subject: [PATCH 119/165] Fix newly found inspections from 2021.1EAP1 --- .../TestSceneMultiplayerRoomManager.cs | 21 ++++++++++++------- .../Screens/TestSceneTeamIntroScreen.cs | 11 +++++----- .../Lounge/Components/DrawableRoom.cs | 8 +++---- .../OnlinePlay/Multiplayer/Multiplayer.cs | 12 +++++------ .../OnlinePlay/OnlinePlaySongSelect.cs | 18 +++++++++------- .../Beatmaps/DifficultyCalculatorTest.cs | 12 +++++++---- 6 files changed, 49 insertions(+), 33 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 91c15de69f..c008771fd9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -141,14 +141,21 @@ namespace osu.Game.Tests.Visual.Multiplayer private Room createRoom(Action initFunc = null) { - var room = new Room(); - - room.Name.Value = "test room"; - room.Playlist.Add(new PlaylistItem + var room = new Room { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, - Ruleset = { Value = Ruleset.Value } - }); + Name = + { + Value = "test room" + }, + Playlist = + { + new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, + Ruleset = { Value = Ruleset.Value } + } + } + }; initFunc?.Invoke(room); return room; diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs index b3f78c92d9..e89aac73fa 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneTeamIntroScreen.cs @@ -17,11 +17,12 @@ namespace osu.Game.Tournament.Tests.Screens [BackgroundDependencyLoader] private void load() { - var match = new TournamentMatch(); - match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); - match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); - match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); - ladder.CurrentMatch.Value = match; + ladder.CurrentMatch.Value = new TournamentMatch + { + Team1 = { Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA") }, + Team2 = { Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN") }, + Round = { Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals") } + }; Add(new TeamIntroScreen { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 0a7198a7fa..35782c6104 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public event Action StateChanged; private readonly Box selectionBox; - private CachedModelDependencyContainer dependencies; [Resolved(canBeNull: true)] private OnlinePlayScreen parentScreen { get; set; } @@ -209,9 +208,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Model.Value = Room; - return dependencies; + return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) + { + Model = { Value = Room } + }; } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index a065d04f64..dbac826954 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -54,12 +54,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } - protected override Room CreateNewRoom() - { - var room = new Room { Name = { Value = $"{API.LocalUser}'s awesome room" } }; - room.Category.Value = RoomCategory.Realtime; - return room; - } + protected override Room CreateNewRoom() => + new Room + { + Name = { Value = $"{API.LocalUser}'s awesome room" }, + Category = { Value = RoomCategory.Realtime } + }; protected override string ScreenTitle => "Multiplayer"; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 3f30ef1176..3e7e557aad 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -96,15 +96,19 @@ namespace osu.Game.Screens.OnlinePlay { itemSelected = true; - var item = new PlaylistItem(); + var item = new PlaylistItem + { + Beatmap = + { + Value = Beatmap.Value.BeatmapInfo + }, + Ruleset = + { + Value = Ruleset.Value + } + }; - item.Beatmap.Value = Beatmap.Value.BeatmapInfo; - item.Ruleset.Value = Ruleset.Value; - - item.RequiredMods.Clear(); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); - - item.AllowedMods.Clear(); item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.CreateCopy())); SelectItem(item); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index e10bf08da4..76f229a799 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -31,12 +31,16 @@ namespace osu.Game.Tests.Beatmaps using (var stream = new LineBufferedReader(resStream)) { var decoder = Decoder.GetDecoder(stream); + ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; - var working = new TestWorkingBeatmap(decoder.Decode(stream)); - working.BeatmapInfo.Ruleset = CreateRuleset().RulesetInfo; - - return working; + return new TestWorkingBeatmap(decoder.Decode(stream)) + { + BeatmapInfo = + { + Ruleset = CreateRuleset().RulesetInfo + } + }; } } From 0f381f7758d567f4dc4796beceec10a02ba67db1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:38:02 +0900 Subject: [PATCH 120/165] Fix wrong code --- osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs | 6 ++++-- .../Objects/Pooling/PoolableDrawableWithLifetime.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs index b5b3cec15d..da0d57f9d1 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs @@ -117,12 +117,14 @@ namespace osu.Game.Tests.Gameplay AddStep("Modify start time", () => entry.HitObject.StartTime = 100); AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeStart", () => dho.LifetimeStart = 666); + AddAssert("Lifetime change is blocked", () => dho.LifetimeStart == double.MinValue); + AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999); AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue); AddStep("KeepAlive = false", () => entry.KeepAlive = false); - AddAssert("Drawable lifetime is restored", () => - dho.LifetimeStart == 100 - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET && dho.LifetimeEnd == 999); + AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999); } private class TestDrawableHitObject : DrawableHitObject diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index b1515e2ac2..fb1baf1d65 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Objects.Pooling // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart = value; + base.LifetimeStart = Entry.LifetimeStart; } } From 00ffea5e2c2b7bfa666d1da7c57e18785a5f6aa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 14:38:30 +0900 Subject: [PATCH 121/165] Update tests to specify full absolute path --- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 1e19af933a..9d8f07969c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestLink() { - AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/"); + AddStep("set current path", () => markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/"); AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_Page"); @@ -113,7 +113,7 @@ needs_cleanup: true AddStep("Add relative image", () => { markdownContainer.DocumentUrl = "https://dev.ppy.sh"; - markdownContainer.CurrentPath = "Interface/"; + markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Interface/"; markdownContainer.Text = "![intro](img/intro-screen.jpg)"; }); } @@ -124,7 +124,7 @@ needs_cleanup: true AddStep("Add paragraph with block image", () => { markdownContainer.DocumentUrl = "https://dev.ppy.sh"; - markdownContainer.CurrentPath = "Interface/"; + markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Interface/"; markdownContainer.Text = @"Line before image ![play menu](img/play-menu.jpg ""Main Menu in osu!"") From 40949f6c1b0c1088f4c28b94b8f9c30084952f69 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 1 Jun 2021 14:46:43 +0900 Subject: [PATCH 122/165] Simplify lifetime setter Setting entry lifetime will cause `LifetimeChanged` event and `base.LifetimeStart`/`End` will be modified in the callback. --- .../Objects/Pooling/PoolableDrawableWithLifetime.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index fb1baf1d65..4440ca8d21 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -35,11 +35,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null && LifetimeStart != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry == null) return; - - // Cannot write it as `base.LifetimeStart = Entry.LifetimeStart = value` because the change may be blocked (when `HitObjectLifetimeEntry.KeepAlive` is true). - Entry.LifetimeStart = value; - base.LifetimeStart = Entry.LifetimeStart; + if (Entry != null) + Entry.LifetimeStart = value; } } @@ -51,10 +48,8 @@ namespace osu.Game.Rulesets.Objects.Pooling if (Entry == null && LifetimeEnd != value) throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); - if (Entry == null) return; - - Entry.LifetimeEnd = value; - base.LifetimeEnd = Entry.LifetimeEnd; + if (Entry != null) + Entry.LifetimeEnd = value; } } From 240f7facba9c365d1d3850f314ce2c4e34a24254 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 15:39:02 +0900 Subject: [PATCH 123/165] Add local concessions for autoplay test handling --- osu.Game/Tests/Visual/TestPlayer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 0addc9de75..99308f8d75 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; @@ -48,6 +49,20 @@ namespace osu.Game.Tests.Visual PauseOnFocusLost = pauseOnFocusLost; } + protected override void PrepareReplay() + { + var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + + if (replayGeneratingMod != null) + { + // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. + DrawableRuleset?.SetReplayScore(replayGeneratingMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); + return; + } + + base.PrepareReplay(); + } + [BackgroundDependencyLoader] private void load() { From 3ba0d29108fc250e9c29ea37ec14a9a900741ebf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 15:44:24 +0900 Subject: [PATCH 124/165] Fix incorrect beatmap being parsed down for autoplay generation --- osu.Game/Screens/Play/ReplayPlayer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index f26675cc64..91236ad607 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -17,7 +18,7 @@ namespace osu.Game.Screens.Play { protected Score Score { get; private set; } - private readonly Func, Score> createScore; + private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) protected override bool CheckModsAllowFailure() => false; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Play { } - public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) + public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null) : base(configuration) { this.createScore = createScore; @@ -38,7 +39,7 @@ namespace osu.Game.Screens.Play { if (!LoadedBeatmapSuccessfully) return; - Score = createScore(GameplayBeatmap, Mods.Value); + Score = createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); } protected override void PrepareReplay() From cd8e3f3a0430402c4bbd9bdb93782a04aa4626ab Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 09:57:43 +0300 Subject: [PATCH 125/165] Revert "Group all skinnable test scenes to one `TestSceneSkinnableHUDComponents`" This reverts commit d1272d5e13b22f4118aa7a5820bc72cc319b7312. --- .../Visual/Gameplay/TestSceneComboCounter.cs | 35 +++++++++ .../TestSceneSkinnableAccuracyCounter.cs | 36 +++++++++ .../TestSceneSkinnableHUDComponents.cs | 76 ------------------- .../TestSceneSkinnableHealthDisplay.cs | 57 ++++++++++++++ .../TestSceneSkinnableScoreCounter.cs | 41 ++++++++++ 5 files changed, 169 insertions(+), 76 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs new file mode 100644 index 0000000000..b22af0f7ac --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneComboCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); + } + + [Test] + public void TestComboCounterIncrementing() + { + AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10); + + AddStep("reset combo", () => scoreProcessor.Combo.Value = 0); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs new file mode 100644 index 0000000000..6f4e6a2420 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); + AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); + } + + [Test] + public void TestChangingAccuracy() + { + AddStep(@"Reset all", () => scoreProcessor.Accuracy.Value = 1); + + AddStep(@"Miss :(", () => scoreProcessor.Accuracy.Value -= 0.023); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs deleted file mode 100644 index 1c2f572d9e..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDComponents.cs +++ /dev/null @@ -1,76 +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.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public class TestSceneSkinnableHUDComponents : SkinnableTestScene - { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - - [Cached] - private ScoreProcessor scoreProcessor = new ScoreProcessor(); - - [Cached(typeof(HealthProcessor))] - private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - - [BackgroundDependencyLoader] - private void load() - { - SetContents(() => new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }); - } - - [Test] - public void TestScoreCounter() - { - AddStep(@"reset total score", () => scoreProcessor.TotalScore.Value = 0); - AddStep(@"increment total score", () => scoreProcessor.TotalScore.Value += 300); - AddStep(@"set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); - } - - [Test] - public void TestComboCounter() - { - AddStep(@"reset combo", () => scoreProcessor.Combo.Value = 0); - AddRepeatStep(@"increase combo", () => scoreProcessor.Combo.Value++, 10); - } - - [Test] - public void TestAccuracyCounter() - { - AddStep(@"reset accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep(@"decrease accuracy", () => scoreProcessor.Accuracy.Value -= 0.023); - } - - [Test] - public void TestHealthDisplay() - { - AddStep(@"reset health", () => healthProcessor.Health.Value = 1); - AddRepeatStep(@"decrease hp", () => healthProcessor.Health.Value -= 0.08f, 10); - AddRepeatStep(@"decrease hp without flash", () => healthProcessor.Health.Value += 0.1f, 3); - AddRepeatStep(@"increase hp with flash", () => - { - healthProcessor.Health.Value += 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); - }, 3); - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs new file mode 100644 index 0000000000..ead27bf017 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableHealthDisplay : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); + AddStep(@"Reset all", delegate + { + healthProcessor.Health.Value = 1; + }); + } + + [Test] + public void TestHealthDisplayIncrementing() + { + AddRepeatStep(@"decrease hp", delegate + { + healthProcessor.Health.Value -= 0.08f; + }, 10); + + AddRepeatStep(@"increase hp without flash", delegate + { + healthProcessor.Health.Value += 0.1f; + }, 3); + + AddRepeatStep(@"increase hp with flash", delegate + { + healthProcessor.Health.Value += 0.1f; + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + }, 3); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs new file mode 100644 index 0000000000..8d633c3ca2 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneSkinnableScoreCounter : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); + } + + [Test] + public void TestScoreCounterIncrementing() + { + AddStep(@"Reset all", () => scoreProcessor.TotalScore.Value = 0); + + AddStep(@"Hit! :D", () => scoreProcessor.TotalScore.Value += 300); + } + + [Test] + public void TestVeryLargeScore() + { + AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); + } + } +} From 1babb05fc7ee51aef25d53468ae7b2d1129a485e Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:03:05 +0700 Subject: [PATCH 126/165] add OsuMarkdownInlineCode --- .../Markdown/OsuMarkdownTextFlowContainer.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index c3527fa99a..267a5befb5 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -4,7 +4,9 @@ using Markdig.Syntax.Inlines; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Overlays; @@ -24,5 +26,32 @@ namespace osu.Game.Graphics.Containers.Markdown protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); + + private class OsuMarkdownInlineCode : Container + { + [Resolved] + private IMarkdownTextComponent parentTextComponent { get; set; } + + public string Text; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + AutoSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background6, + }, + parentTextComponent.CreateSpriteText().With(t => + { + t.Colour = colourProvider.Light1; + t.Text = Text; + }), + }; + } + } } } From 5108dadfbc0d7fb8aeb0139a993bd40bf05f7ff2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:03:57 +0700 Subject: [PATCH 127/165] use inline code in markdown text flow --- .../Markdown/OsuMarkdownTextFlowContainer.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index 267a5befb5..b9c2b20bb6 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -14,15 +14,14 @@ namespace osu.Game.Graphics.Containers.Markdown { public class OsuMarkdownTextFlowContainer : MarkdownTextFlowContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - protected override void AddLinkText(string text, LinkInline linkInline) => AddDrawable(new OsuMarkdownLinkText(text, linkInline)); - // TODO : Add background (colour B6) and change font to monospace - protected override void AddCodeInLine(CodeInline codeInline) - => AddText(codeInline.Content, t => { t.Colour = colourProvider.Light1; }); + // TODO : Change font to monospace + protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode + { + Text = codeInline.Content + }); protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic) => CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic)); From b4dd93553892db0e43c52a6c3b7235f2aba8c41c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 1 Jun 2021 14:14:12 +0700 Subject: [PATCH 128/165] add corner radius and padding --- .../Containers/Markdown/OsuMarkdownTextFlowContainer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index b9c2b20bb6..f3308019ce 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -37,6 +37,8 @@ namespace osu.Game.Graphics.Containers.Markdown private void load(OverlayColourProvider colourProvider) { AutoSizeAxes = Axes.Both; + CornerRadius = 4; + Masking = true; Children = new Drawable[] { new Box @@ -48,6 +50,11 @@ namespace osu.Game.Graphics.Containers.Markdown { t.Colour = colourProvider.Light1; t.Text = Text; + t.Padding = new MarginPadding + { + Vertical = 1, + Horizontal = 4, + }; }), }; } From c090110ae2c4d828311728b25592861afce8bffc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 10:13:56 +0300 Subject: [PATCH 129/165] Provide cell skin on content creation --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 3d2c68c2ad..b1287fd012 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -52,7 +52,9 @@ namespace osu.Game.Tests.Visual private readonly List createdDrawables = new List(); - public void SetContents(Func creationFunction) + public void SetContents(Func creationFunction) => SetContents(_ => creationFunction?.Invoke()); + + public void SetContents(Func creationFunction) { createdDrawables.Clear(); @@ -67,9 +69,9 @@ namespace osu.Game.Tests.Visual protected IEnumerable CreatedDrawables => createdDrawables; - private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap) + private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap) { - var created = creationFunction(); + var created = creationFunction(skin); createdDrawables.Add(created); From cb38abab3566d304a239826a7feb276f500a087b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 1 Jun 2021 10:16:01 +0300 Subject: [PATCH 130/165] Add local logic for creating default/legacy implementation based on cell skin --- .../SkinnableHUDComponentTestScene.cs | 33 +++++++++++++++++++ .../TestSceneSkinnableAccuracyCounter.cs | 12 +++---- ...r.cs => TestSceneSkinnableComboCounter.cs} | 17 +++------- .../TestSceneSkinnableHealthDisplay.cs | 12 +++---- .../TestSceneSkinnableScoreCounter.cs | 16 +++------ 5 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs rename osu.Game.Tests/Visual/Gameplay/{TestSceneComboCounter.cs => TestSceneSkinnableComboCounter.cs} (56%) diff --git a/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs b/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs new file mode 100644 index 0000000000..6de9d7c478 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/SkinnableHUDComponentTestScene.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public abstract class SkinnableHUDComponentTestScene : SkinnableTestScene + { + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); + + [SetUp] + public void SetUp() => Schedule(() => + { + SetContents(skin => + { + var implementation = skin != null + ? CreateLegacyImplementation() + : CreateDefaultImplementation(); + + implementation.Anchor = Anchor.Centre; + implementation.Origin = Anchor.Centre; + return implementation; + }); + }); + + protected abstract Drawable CreateDefaultImplementation(); + protected abstract Drawable CreateLegacyImplementation(); + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs index 6f4e6a2420..80eb887894 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -3,26 +3,26 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene + public class TestSceneSkinnableAccuracyCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); + protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter(); + [SetUpSteps] public void SetUpSteps() { AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); } [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs similarity index 56% rename from osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs index b22af0f7ac..1c5a05dd1d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableComboCounter.cs @@ -3,26 +3,19 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneComboCounter : SkinnableTestScene + public class TestSceneSkinnableComboCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); - } + protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter(); [Test] public void TestComboCounterIncrementing() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index ead27bf017..057798c922 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -3,28 +3,28 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableHealthDisplay : SkinnableTestScene + public class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay(); + protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay(); + [SetUpSteps] public void SetUpSteps() { - AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); AddStep(@"Reset all", delegate { healthProcessor.Health.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs index 8d633c3ca2..1700886263 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -3,26 +3,20 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Testing; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Osu; +using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { - public class TestSceneSkinnableScoreCounter : SkinnableTestScene + public class TestSceneSkinnableScoreCounter : SkinnableHUDComponentTestScene { - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); - [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor(); - [SetUpSteps] - public void SetUpSteps() - { - AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); - } + protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter(); + protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter(); [Test] public void TestScoreCounterIncrementing() From 790f1dacc9839d0267797821bb59042c884b549d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:24:38 +0900 Subject: [PATCH 131/165] Ensure `ScoreProcessor` is still hooked up in special case --- osu.Game/Tests/Visual/TestPlayer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 99308f8d75..b7e1c68c89 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -51,12 +51,16 @@ namespace osu.Game.Tests.Visual protected override void PrepareReplay() { - var replayGeneratingMod = Mods.Value.OfType().FirstOrDefault(); + var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - if (replayGeneratingMod != null) + // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. + if (autoplayMod != null) { - // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. - DrawableRuleset?.SetReplayScore(replayGeneratingMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); + var replayScore = autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); + + DrawableRuleset?.SetReplayScore(replayScore); + + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(replayScore.ScoreInfo); return; } From 6e861a9b7fcb107e2f12887ee3f4b93e253beedd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:24:38 +0900 Subject: [PATCH 132/165] Revert incorrect `ScoreProcessor` change --- osu.Game/Tests/Visual/TestPlayer.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index b7e1c68c89..ceb886f9c4 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -56,11 +56,7 @@ namespace osu.Game.Tests.Visual // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. if (autoplayMod != null) { - var replayScore = autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); - - DrawableRuleset?.SetReplayScore(replayScore); - - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(replayScore.ScoreInfo); + DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); return; } From 145e42928b08e22902fe74c9e8559e82a39c649f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:46:29 +0900 Subject: [PATCH 133/165] Fix remaining null checks --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 4 ++-- osu.Game/OsuGame.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 054febeec3..f89d7fd779 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics.Containers AddText(text[previousLinkEnd..link.Index]); string displayText = text.Substring(link.Index, link.Length); - string linkArgument = link.Argument ?? link.Url; + string linkArgument = link.Argument; string tooltip = displayText == link.Url ? null : link.Url; AddLink(displayText, link.Action, linkArgument, tooltip); @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, null), tooltipText, action); + => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, @"action"), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b3b0773eff..c51624341e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -277,7 +277,7 @@ namespace osu.Game { case LinkAction.OpenBeatmap: // TODO: proper query params handling - if (link.Argument != null && int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId)) + if (int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId)) ShowBeatmap(beatmapId); break; From 7a71cc1e821e0346e1d9c19fc4506388928b9ee3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 16:54:29 +0900 Subject: [PATCH 134/165] Fix actually incorrect navigation test (can no longer retry from autoplay results) --- osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 253e448bb4..dd05ce9b7e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("set autoplay", () => Game.SelectedMods.Value = new[] { new OsuModAutoplay() }); + AddStep("set nofail", () => Game.SelectedMods.Value = new[] { new OsuModNoFail() }); AddStep("press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); From 3668f1861f3369a3819da6d44dd52c648f54c494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:10:09 +0900 Subject: [PATCH 135/165] Fix one more null issue --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index f89d7fd779..458bc8876f 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText); } - public void AddLink(IEnumerable text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) + public void AddLink(IEnumerable text, LinkAction action, string linkArgument, string tooltipText = null) { foreach (var t in text) AddArbitraryDrawable(t); From f14c0eae9956641f8c77179f165b059abc98914d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 17:28:41 +0900 Subject: [PATCH 136/165] Fix editor no longer creating autoplay frames --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a2dade2627..0667145ffb 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -349,6 +349,9 @@ namespace osu.Game.Rulesets.UI foreach (var mod in mods.OfType>()) mod.ApplyToDrawableRuleset(this); + foreach (var mod in mods.OfType()) + SetReplayScore(mod.CreateReplayScore(Beatmap, mods)); + foreach (var mod in mods.OfType()) mod.ReadFromConfig(config); } From 92ffc4627f8bf90f3320dd71a6b20495e071ba1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Jun 2021 23:24:49 +0900 Subject: [PATCH 137/165] 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 3d51357d8b..e95c7e6619 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d299ba4fda..c9bfb4a8eb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -34,7 +34,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 9e178b267a..70bec9b894 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 97d2ac19cfe679a27368b90d9e3510a214af01f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:09 +0000 Subject: [PATCH 138/165] Bump BenchmarkDotNet from 0.12.1 to 0.13.0 Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.12.1 to 0.13.0. - [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases) - [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.12.1...v0.13.0) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index bfcf4ef35e..7a74563b2b 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -7,7 +7,7 @@ - + From 069baab8839c5da5f710f0d50e2cfc8eefaff870 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 139/165] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..169c28480f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + From ea9919e8fe023f4b90d101d69ddd1eff96b2992b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 140/165] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.5 to 5.0.6 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..fb675da847 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + From 967a7c3db5918d10223728d0a5b646ab7b6c8392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:27 +0000 Subject: [PATCH 141/165] Bump Microsoft.NET.Test.Sdk from 16.9.4 to 16.10.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.9.4 to 16.10.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.9.4...v16.10.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 992f954a3a..5eb5efa54c 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 7571d1827a..d7c116411a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 1c8ed54440..89b551286b 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 7571d1827a..d7c116411a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 77e9d672e3..83d0744588 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 8f8b99b092..b2a0912d19 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index e01e858873..ebe642803b 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 2dfa1dfbb7..8fb167ba10 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 895518e1b9..35d3c7f202 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index d5dda39aa5..2084be765a 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From bd8db2ce63869e243cccd2efae812891f075e020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:28 +0000 Subject: [PATCH 142/165] Bump ppy.LocalisationAnalyser from 2021.524.0 to 2021.525.0 Bumps [ppy.LocalisationAnalyser](https://github.com/ppy/osu-localisation-analyser) from 2021.524.0 to 2021.525.0. - [Release notes](https://github.com/ppy/osu-localisation-analyser/releases) - [Commits](https://github.com/ppy/osu-localisation-analyser/compare/2021.524.0...2021.525.0) --- updated-dependencies: - dependency-name: ppy.LocalisationAnalyser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..c8eea7c4f3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From deaa381440f0f8e02dd3809da69efa4948975b50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:30 +0000 Subject: [PATCH 143/165] Bump Sentry from 3.3.4 to 3.4.0 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.3.4 to 3.4.0. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.3.4...3.4.0) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..967f0af1df 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From a10ef2ba6513cb617886270b2bd6792d7b06819a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:31 +0000 Subject: [PATCH 144/165] Bump Humanizer from 2.8.26 to 2.10.1 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.26 to 2.10.1. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/main/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.26...v2.10.1) --- updated-dependencies: - dependency-name: Humanizer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..28b77a32ec 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 70bec9b894..c1d63f3236 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -89,7 +89,7 @@ - + From a758b382784ac346764d3b5d803044fc96cf2f78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:59:32 +0000 Subject: [PATCH 145/165] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.5 to 5.0.6. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.5...v5.0.6) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9bfb4a8eb..12f8163bfa 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From 8a76d97b63a65fe4e303525e4bd09d809cafa2f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:06:30 +0900 Subject: [PATCH 146/165] Remove replay logic from `DrawableRuleset` (and implement in `DrawableEditorRulesetWrapper`) --- .../Rulesets/Edit/DrawableEditorRulesetWrapper.cs | 14 +++++++++++--- osu.Game/Rulesets/UI/DrawableRuleset.cs | 12 +----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs index 8166e6b8ce..071f01ca00 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit; @@ -52,15 +54,21 @@ namespace osu.Game.Rulesets.Edit if (changeHandler != null) { // for now only regenerate replay on a finalised state change, not HitObjectUpdated. - changeHandler.OnStateChange += updateReplay; + changeHandler.OnStateChange += () => Scheduler.AddOnce(regenerateAutoplay); } else { - beatmap.HitObjectUpdated += _ => updateReplay(); + beatmap.HitObjectUpdated += _ => Scheduler.AddOnce(regenerateAutoplay); } + + Scheduler.AddOnce(regenerateAutoplay); } - private void updateReplay() => Scheduler.AddOnce(drawableRuleset.RegenerateAutoplay); + private void regenerateAutoplay() + { + var autoplayMod = drawableRuleset.Mods.OfType().Single(); + drawableRuleset.SetReplayScore(autoplayMod.CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods)); + } private void addHitObject(HitObject hitObject) { diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 0667145ffb..16a411d478 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -182,18 +182,11 @@ namespace osu.Game.Rulesets.UI .WithChild(ResumeOverlay))); } - RegenerateAutoplay(); + applyRulesetMods(Mods, config); loadObjects(cancellationToken ?? default); } - public void RegenerateAutoplay() - { - // for now this is applying mods which aren't just autoplay. - // we'll need to reconsider this flow in the future. - applyRulesetMods(Mods, config); - } - /// /// Creates and adds drawable representations of hit objects to the play field. /// @@ -349,9 +342,6 @@ namespace osu.Game.Rulesets.UI foreach (var mod in mods.OfType>()) mod.ApplyToDrawableRuleset(this); - foreach (var mod in mods.OfType()) - SetReplayScore(mod.CreateReplayScore(Beatmap, mods)); - foreach (var mod in mods.OfType()) mod.ReadFromConfig(config); } From 911256603bf0ba6d83c05694c6d20ecdbafa041b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:10:02 +0900 Subject: [PATCH 147/165] Rewrite comment to hopefully be more informative --- osu.Game/Tests/Visual/TestPlayer.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index ceb886f9c4..09da4db952 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -51,9 +51,15 @@ namespace osu.Game.Tests.Visual protected override void PrepareReplay() { + // Generally, replay generation is handled by whatever is constructing the player. + // This is implemented locally here to ease migration of test scenes that have some executions + // running with autoplay and some not, but are not written in a way that lends to instantiating + // different `Player` types. + // + // Eventually we will want to remove this and update all test usages which rely on autoplay to use + // a `TestReplayPlayer`. var autoplayMod = Mods.Value.OfType().FirstOrDefault(); - // This logic should really not exist (and tests should be instantiating a ReplayPlayer), but a lot of base work is required to make that happen. if (autoplayMod != null) { DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayBeatmap.PlayableBeatmap, Mods.Value)); From d3d8941ec8559a173c23ff63da47554681124e31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 2 Jun 2021 11:11:41 +0900 Subject: [PATCH 148/165] Make method internal --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 5ad0d6246e..096dad88bd 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -94,6 +94,6 @@ namespace osu.Game.Rulesets.Objects /// /// Set using . /// - public void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; + internal void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } } From b82190e157deb303bf15ba69b56fb5a55ae4584d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 2 Jun 2021 11:32:24 +0900 Subject: [PATCH 149/165] Fix hit circle animation reset when skin is changed The transforms applied in `Animate` call was not applied because the piece is recreated. --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 2 -- osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs | 4 ++++ .../Skinning/Legacy/LegacyMainCirclePiece.cs | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 1bf9e76d7d..236af4b3f1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -182,8 +182,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // todo: temporary / arbitrary, used for lifetime optimisation. this.Delay(800).FadeOut(); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index b46baa00ba..9af9d2ea95 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private readonly IBindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + armedState.BindTo(drawableObject.State); } protected override void LoadComplete() @@ -67,6 +69,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default }, true); indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); + + armedState.BindValueChanged(state => Animate(state.NewValue), true); } public void Animate(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index cf62165929..57af247562 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + private readonly IBindable armedState = new Bindable(); [Resolved] private DrawableHitObject drawableObject { get; set; } @@ -115,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); + armedState.BindTo(drawableObject.State); Texture getTextureWithFallback(string name) { @@ -139,6 +141,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); + + armedState.BindValueChanged(state => Animate(state.NewValue), true); } public void Animate(ArmedState state) From c06ff3c623321a15ad318fafb89b7d3d5c5632ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 2 Jun 2021 11:46:28 +0900 Subject: [PATCH 150/165] Remove defunct `Animate` calls --- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 -- .../Objects/Drawables/DrawableSliderTail.cs | 2 -- .../Skinning/Default/IMainCirclePiece.cs | 17 ----------------- .../Skinning/Default/MainCirclePiece.cs | 12 +++++++----- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 13 +++++++------ 5 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 7b4188edab..b7458b5695 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateHitStateTransforms(state); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index cd6bf1d8d2..ec1387eb54 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Debug.Assert(HitObject.HitWindows != null); - (CirclePiece.Drawable as IMainCirclePiece)?.Animate(state); - switch (state) { case ArmedState.Idle: diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs deleted file mode 100644 index 17a1e29094..0000000000 --- a/osu.Game.Rulesets.Osu/Skinning/Default/IMainCirclePiece.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Skinning.Default -{ - public interface IMainCirclePiece - { - /// - /// Begins animating this . - /// - /// The of the related . - void Animate(ArmedState state); - } -} diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs index 9af9d2ea95..b52dc749f0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/MainCirclePiece.cs @@ -13,7 +13,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Default { - public class MainCirclePiece : CompositeDrawable, IMainCirclePiece + public class MainCirclePiece : CompositeDrawable { private readonly CirclePiece circle; private readonly RingPiece ring; @@ -70,17 +70,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(state => Animate(state.NewValue), true); + armedState.BindValueChanged(animate, true); } - public void Animate(ArmedState state) + private void animate(ValueChangedEvent state) { + ClearTransforms(true); + using (BeginAbsoluteSequence(drawableObject.StateUpdateTime)) glow.FadeOut(400); using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state) + switch (state.NewValue) { case ArmedState.Hit: const double flash_in = 40; @@ -93,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default explode.FadeIn(flash_in); this.ScaleTo(1.5f, 400, Easing.OutQuad); - using (BeginDelayedSequence(flash_in, true)) + using (BeginDelayedSequence(flash_in)) { // after the flash, we can hide some elements that were behind it ring.FadeOut(); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 57af247562..ffbeea5e0e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -20,7 +19,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece + public class LegacyMainCirclePiece : CompositeDrawable { private readonly string priorityLookup; private readonly bool hasNumber; @@ -142,16 +141,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - armedState.BindValueChanged(state => Animate(state.NewValue), true); + armedState.BindValueChanged(animate, true); } - public void Animate(ArmedState state) + private void animate(ValueChangedEvent state) { const double legacy_fade_duration = 240; - using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true)) + ClearTransforms(true); + + using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime)) { - switch (state) + switch (state.NewValue) { case ArmedState.Hit: circleSprites.FadeOut(legacy_fade_duration, Easing.Out); From 45984f035b731ff0d39510b22df7375455fafc39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 11:33:43 +0900 Subject: [PATCH 151/165] Make autoplay tests test via the `ReplayPlayer` code path --- .../Visual/Gameplay/TestSceneAutoplay.cs | 4 +- osu.Game/Tests/Visual/TestReplayPlayer.cs | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Tests/Visual/TestReplayPlayer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index e47c782bca..bc7cf8eee2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -18,12 +18,12 @@ namespace osu.Game.Tests.Visual.Gameplay [Description("Player instantiated with an autoplay mod.")] public class TestSceneAutoplay : TestSceneAllRulesetPlayers { - protected new TestPlayer Player => (TestPlayer)base.Player; + protected new TestReplayPlayer Player => (TestReplayPlayer)base.Player; protected override Player CreatePlayer(Ruleset ruleset) { SelectedMods.Value = new[] { ruleset.GetAutoplayMod() }; - return new TestPlayer(false); + return new TestReplayPlayer(false); } protected override void AddCheckSteps() diff --git a/osu.Game/Tests/Visual/TestReplayPlayer.cs b/osu.Game/Tests/Visual/TestReplayPlayer.cs new file mode 100644 index 0000000000..ac47d186eb --- /dev/null +++ b/osu.Game/Tests/Visual/TestReplayPlayer.cs @@ -0,0 +1,75 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual +{ + /// + /// A player that exposes many components that would otherwise not be available, for testing purposes. + /// + public class TestReplayPlayer : ReplayPlayer + { + protected override bool PauseOnFocusLost { get; } + + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + /// + /// Mods from *player* (not OsuScreen). + /// + public new Bindable> Mods => base.Mods; + + public new HUDOverlay HUDOverlay => base.HUDOverlay; + + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + public new HealthProcessor HealthProcessor => base.HealthProcessor; + + public new bool PauseCooldownActive => base.PauseCooldownActive; + + public readonly List Results = new List(); + + /// + /// Instantiate a replay player that renders an autoplay mod. + /// + public TestReplayPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) + : base((beatmap, mods) => mods.OfType().First().CreateReplayScore(beatmap, mods), new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) + { + PauseOnFocusLost = pauseOnFocusLost; + } + + /// + /// Instantiate a replay player that renders the provided replay. + /// + public TestReplayPlayer(Score score, bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) + : base(score, new PlayerConfiguration + { + AllowPause = allowPause, + ShowResults = showResults + }) + { + PauseOnFocusLost = pauseOnFocusLost; + } + + [BackgroundDependencyLoader] + private void load() + { + ScoreProcessor.NewJudgement += r => Results.Add(r); + } + } +} From 82c1a9ae4c6bfdbbd95945f33b823faaf33354fa Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 1 Jun 2021 18:59:31 -0700 Subject: [PATCH 152/165] Remove windows-only note from InspectCode section of readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb790ca18f..3054f19e79 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself. -JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice. +JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice. ## Contributing From ac761bb0755bb0f377a1c57a09688354b6fea5a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 14:43:35 +0900 Subject: [PATCH 153/165] Use `string.Empty` instead of arbitrary string --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 458bc8876f..5ff2fdf6b2 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, @"action"), tooltipText, action); + => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); From bf216687e1b74be0623d1a7cf9af698a1ebba37c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:05:06 +0900 Subject: [PATCH 154/165] Fix osu!stable directory selection failing if no `Songs` folder is present at install location --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 4a28ab3722..a66c7e6d3b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -57,7 +57,7 @@ namespace osu.Desktop private string getStableInstallPath() { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists("osu!.cfg"); string stableInstallPath; From d5d5a4d0d799700f7805df49ef86322e9d6a33a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:13:21 +0900 Subject: [PATCH 155/165] Actually combine the path --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index a66c7e6d3b..4de1e84fbf 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -57,7 +57,7 @@ namespace osu.Desktop private string getStableInstallPath() { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists("osu!.cfg"); + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg")); string stableInstallPath; From a15cac6f5386651adf0af91d990f94525da98b99 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 15:44:04 +0900 Subject: [PATCH 156/165] Change the way `Score` is initialised in `Player` to better lend to population of metadata --- .../Spectate/MultiSpectatorPlayer.cs | 4 +- .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 8 +- osu.Game/Screens/Play/Player.cs | 73 ++++++++----------- osu.Game/Screens/Play/ReplayPlayer.cs | 24 +----- osu.Game/Screens/Play/SpectatorPlayer.cs | 12 +-- 5 files changed, 45 insertions(+), 76 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 0fe9e01d9d..2c157b0564 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs @@ -17,7 +17,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public class MultiSpectatorPlayer : SpectatorPlayer { private readonly Bindable waitingOnFrames = new Bindable(true); - private readonly Score score; private readonly ISpectatorPlayerClock spectatorPlayerClock; /// @@ -28,7 +27,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public MultiSpectatorPlayer([NotNull] Score score, [NotNull] ISpectatorPlayerClock spectatorPlayerClock) : base(score) { - this.score = score; this.spectatorPlayerClock = spectatorPlayerClock; } @@ -43,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate base.UpdateAfterChildren(); // This is required because the frame stable clock is set to WaitingOnFrames = false for one frame. - waitingOnFrames.Value = DrawableRuleset.FrameStableClock.WaitingOnFrames.Value || score.Replay.Frames.Count == 0; + waitingOnFrames.Value = DrawableRuleset.FrameStableClock.WaitingOnFrames.Value || Score.Replay.Frames.Count == 0; } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 260d4961ff..a2ef715367 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -54,11 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return new PlaylistsResultsScreen(score, RoomId.Value.Value, PlaylistItem, true); } - protected override Score CreateScore() + protected override void PrepareScoreForResults() { - var score = base.CreateScore(); - score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); - return score; + base.PrepareScoreForResults(); + + Score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 99570f6c5e..0d1ebd30fc 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -24,10 +23,8 @@ using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Online.Spectator; using osu.Game.Overlays; -using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -137,6 +134,8 @@ namespace osu.Game.Screens.Play public readonly PlayerConfiguration Configuration; + protected Score Score { get; private set; } + /// /// Create a new player instance. /// @@ -161,22 +160,32 @@ namespace osu.Game.Screens.Play if (!LoadedBeatmapSuccessfully) return; + Score = CreateScore(); + + // ensure the score is in a consistent state with the current player. + Score.ScoreInfo.Beatmap = Beatmap.Value.BeatmapInfo; + Score.ScoreInfo.Ruleset = rulesetInfo; + Score.ScoreInfo.Mods = Mods.Value.ToArray(); + PrepareReplay(); + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); + gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true); } - [CanBeNull] - private Score recordingScore; - /// /// Run any recording / playback setup for replays. /// protected virtual void PrepareReplay() { - DrawableRuleset.SetRecordTarget(recordingScore = new Score()); + DrawableRuleset.SetRecordTarget(Score); + } - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(recordingScore.ScoreInfo); + protected virtual void PrepareScoreForResults() + { + // perform one final population to ensure everything is up-to-date. + ScoreProcessor.PopulateScore(Score.ScoreInfo); } [BackgroundDependencyLoader(true)] @@ -631,11 +640,11 @@ namespace osu.Game.Screens.Play prepareScoreForDisplayTask ??= Task.Run(async () => { - var score = CreateScore(); + PrepareScoreForResults(); try { - await PrepareScoreForResultsAsync(score).ConfigureAwait(false); + await PrepareScoreForResultsAsync(Score).ConfigureAwait(false); } catch (Exception ex) { @@ -644,14 +653,14 @@ namespace osu.Game.Screens.Play try { - await ImportScore(score).ConfigureAwait(false); + await ImportScore(Score).ConfigureAwait(false); } catch (Exception ex) { Logger.Error(ex, "Score import failed!"); } - return score.ScoreInfo; + return Score.ScoreInfo; }); if (skipStoryboardOutro) @@ -903,41 +912,19 @@ namespace osu.Game.Screens.Play } /// - /// Creates the player's . + /// Creates the player's . /// - /// The . - protected virtual Score CreateScore() - { - var score = new Score + /// The . + protected virtual Score CreateScore() => + new Score { - ScoreInfo = new ScoreInfo - { - Beatmap = Beatmap.Value.BeatmapInfo, - Ruleset = rulesetInfo, - Mods = Mods.Value.ToArray(), - } + ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, }; - if (DrawableRuleset.ReplayScore != null) - { - score.ScoreInfo.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser(); - score.Replay = DrawableRuleset.ReplayScore.Replay; - } - else - { - score.ScoreInfo.User = api.LocalUser.Value; - score.Replay = new Replay { Frames = recordingScore?.Replay.Frames.ToList() ?? new List() }; - } - - ScoreProcessor.PopulateScore(score.ScoreInfo); - - return score; - } - /// - /// Imports the player's to the local database. + /// Imports the player's to the local database. /// - /// The to import. + /// The to import. /// The imported score. protected virtual async Task ImportScore(Score score) { @@ -968,9 +955,9 @@ namespace osu.Game.Screens.Play } /// - /// Prepare the for display at results. + /// Prepare the for display at results. /// - /// The to prepare. + /// The to prepare. /// A task that prepares the provided score. On completion, the score is assumed to be ready for display. protected virtual Task PrepareScoreForResultsAsync(Score score) => Task.CompletedTask; diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 91236ad607..e440e7d34e 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Input.Bindings; @@ -16,8 +15,6 @@ namespace osu.Game.Screens.Play { public class ReplayPlayer : Player, IKeyBindingHandler { - protected Score Score { get; private set; } - private readonly Func, Score> createScore; // Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108) @@ -34,28 +31,15 @@ namespace osu.Game.Screens.Play this.createScore = createScore; } - [BackgroundDependencyLoader] - private void load() - { - if (!LoadedBeatmapSuccessfully) return; - - Score = createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); - } - protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); + + // todo: move to base class along with Score? + ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); } - protected override Score CreateScore() - { - var baseScore = base.CreateScore(); - - // Since the replay score doesn't contain statistics, we'll pass them through here. - Score.ScoreInfo.HitEvents = baseScore.ScoreInfo.HitEvents; - - return Score; - } + protected override Score CreateScore() => createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); // Don't re-import replay scores as they're already present in the database. protected override Task ImportScore(Score score) => Task.CompletedTask; diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index a8125dfded..67471dff90 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -18,6 +18,9 @@ namespace osu.Game.Screens.Play { private readonly Score score; + [Resolved] + private SpectatorClient spectatorClient { get; set; } + protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap public SpectatorPlayer(Score score) @@ -25,13 +28,10 @@ namespace osu.Game.Screens.Play this.score = score; } - protected override ResultsScreen CreateResults(ScoreInfo score) - { - return new SpectatorResultsScreen(score); - } + protected override Score CreateScore() => score; - [Resolved] - private SpectatorClient spectatorClient { get; set; } + protected override ResultsScreen CreateResults(ScoreInfo score) + => new SpectatorResultsScreen(score); [BackgroundDependencyLoader] private void load() From f8ae70e562c7d59bcf3b4c04f57dfe09e7d20e0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 16:04:53 +0900 Subject: [PATCH 157/165] Update existing function type rather than adding an override --- .editorconfig | 10 +-- .../TestSceneCatcherArea.cs | 2 +- .../TestSceneComboCounter.cs | 2 +- .../TestSceneFruitObjects.cs | 24 +++--- .../TestSceneFruitVisualChange.cs | 4 +- .../Skinning/ManiaHitObjectTestScene.cs | 2 +- .../Skinning/TestSceneColumnBackground.cs | 2 +- .../Skinning/TestSceneColumnHitObjectArea.cs | 2 +- .../Skinning/TestSceneDrawableJudgement.cs | 2 +- .../Skinning/TestSceneHitExplosion.cs | 2 +- .../Skinning/TestSceneKeyArea.cs | 2 +- .../Skinning/TestScenePlayfield.cs | 4 +- .../Skinning/TestSceneStage.cs | 2 +- .../Skinning/TestSceneStageBackground.cs | 2 +- .../Skinning/TestSceneStageForeground.cs | 2 +- .../TestSceneDrawableJudgement.cs | 2 +- .../TestSceneGameplayCursor.cs | 2 +- .../TestSceneHitCircle.cs | 24 +++--- .../TestSceneSlider.cs | 78 +++++++++---------- .../TestSceneSpinner.cs | 12 +-- .../Skinning/TestSceneDrawableBarLine.cs | 4 +- .../Skinning/TestSceneDrawableDrumRoll.cs | 4 +- .../Skinning/TestSceneDrawableHit.cs | 8 +- .../Skinning/TestSceneDrawableTaikoMascot.cs | 14 ++-- .../Skinning/TestSceneHitExplosion.cs | 8 +- .../Skinning/TestSceneInputDrum.cs | 2 +- .../Skinning/TestSceneKiaiHitExplosion.cs | 4 +- .../Skinning/TestSceneTaikoPlayfield.cs | 2 +- .../Skinning/TestSceneTaikoScroller.cs | 2 +- .../TestSceneDrawableStoryboardSprite.cs | 6 +- .../TestSceneSkinEditorComponentsList.cs | 2 +- .../TestSceneSkinEditorMultipleSkins.cs | 2 +- .../Gameplay/TestSceneSkinnableHUDOverlay.cs | 2 +- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 +- 34 files changed, 117 insertions(+), 129 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0cdf3b92d3..f4d7e08d08 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,14 +10,6 @@ trim_trailing_whitespace = true #Roslyn naming styles -#PascalCase for public and protected members -dotnet_naming_style.pascalcase.capitalization = pascal_case -dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected -dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event -dotnet_naming_rule.public_members_pascalcase.severity = error -dotnet_naming_rule.public_members_pascalcase.symbols = public_members -dotnet_naming_rule.public_members_pascalcase.style = pascalcase - #camelCase for private members dotnet_naming_style.camelcase.capitalization = camel_case @@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none dotnet_diagnostic.CA2225.severity = none # Banned APIs -dotnet_diagnostic.RS0030.severity = error \ No newline at end of file +dotnet_diagnostic.RS0030.severity = error diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index ad404e1f63..4af5098451 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests CircleSize = circleSize }; - SetContents(() => + SetContents(_ => { var droppedObjectContainer = new Container { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs index c7b322c8a0..064a84cb98 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneComboCounter.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { scoreProcessor = new ScoreProcessor(); - SetContents(() => new CatchComboDisplay + SetContents(_ => new CatchComboDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs index 3a651605d3..943adbef52 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs @@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests { base.LoadComplete(); - AddStep("show pear", () => SetContents(() => createDrawableFruit(0))); - AddStep("show grape", () => SetContents(() => createDrawableFruit(1))); - AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2))); - AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3))); + AddStep("show pear", () => SetContents(_ => createDrawableFruit(0))); + AddStep("show grape", () => SetContents(_ => createDrawableFruit(1))); + AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2))); + AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3))); - AddStep("show banana", () => SetContents(createDrawableBanana)); + AddStep("show banana", () => SetContents(_ => createDrawableBanana())); - AddStep("show droplet", () => SetContents(() => createDrawableDroplet())); - AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet)); + AddStep("show droplet", () => SetContents(_ => createDrawableDroplet())); + AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet())); - AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true))); - AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true))); - AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true))); - AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true))); + AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true))); + AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true))); + AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true))); + AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true))); - AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true))); + AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true))); } private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) => diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs index 125e0c674c..9446e864a1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitVisualChange.cs @@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests protected override void LoadComplete() { - AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit + AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit { IndexInBeatmapBindable = { BindTarget = indexInBeatmap }, HyperDashBindable = { BindTarget = hyperDash }, })))); - AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet + AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet { HyperDashBindable = { BindTarget = hyperDash }, })))); diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs index 96444fd316..b7d7af6b8c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/ManiaHitObjectTestScene.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [SetUp] public void SetUp() => Schedule(() => { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs index ca323b5911..106b2d188d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnBackground.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs index 4392666cb7..215f8fb1d5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneColumnHitObjectArea.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs index dcb25f21ba..75a5495078 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneDrawableJudgement.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning { if (hitWindows.IsHitResultAllowed(result)) { - AddStep("Show " + result.GetDescription(), () => SetContents(() => + AddStep("Show " + result.GetDescription(), () => SetContents(_ => new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()) { Type = result diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs index 4dc6700786..004793e1e5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => + SetContents(_ => { var pool = new DrawablePool(5); hitExplosionPools.Add(pool); diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs index c58c07c83b..7564bd84ad 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneKeyArea.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new FillFlowContainer + SetContents(_ => new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs index 161eda650e..c7dc5fc8b5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestScenePlayfield.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning new StageDefinition { Columns = 2 } }; - SetContents(() => new ManiaPlayfield(stageDefinitions)); + SetContents(_ => new ManiaPlayfield(stageDefinitions)); }); } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning new StageDefinition { Columns = 2 } }; - SetContents(() => new ManiaPlayfield(stageDefinitions)); + SetContents(_ => new ManiaPlayfield(stageDefinitions)); }); } diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs index 37b97a444a..7804261906 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStage.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => + SetContents(_ => { ManiaAction normalAction = ManiaAction.Key1; ManiaAction specialAction = ManiaAction.Special1; diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs index a15fb392d6..410a43fc73 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageBackground.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }), + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }), _ => new DefaultStageBackground()) { Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs index bceee1c599..27e97152bc 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneStageForeground.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null) + SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 4395ca6281..7821ae9cf0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests { int poolIndex = 0; - SetContents(() => + SetContents(_ => { DrawablePool pool; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index 9a77292aff..a95159ce4c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void loadContent(bool automated = true, Func skinProvider = null) { - SetContents(() => + SetContents(_ => { var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo); var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 1278a0ff2d..58e46b6687 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestVariousHitCircles() { - AddStep("Miss Big Single", () => SetContents(() => testSingle(2))); - AddStep("Miss Medium Single", () => SetContents(() => testSingle(5))); - AddStep("Miss Small Single", () => SetContents(() => testSingle(7))); - AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true))); - AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true))); - AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true))); - AddStep("Miss Big Stream", () => SetContents(() => testStream(2))); - AddStep("Miss Medium Stream", () => SetContents(() => testStream(5))); - AddStep("Miss Small Stream", () => SetContents(() => testStream(7))); - AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true))); - AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true))); - AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true))); + AddStep("Miss Big Single", () => SetContents(_ => testSingle(2))); + AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5))); + AddStep("Miss Small Single", () => SetContents(_ => testSingle(7))); + AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true))); + AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true))); + AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true))); + AddStep("Miss Big Stream", () => SetContents(_ => testStream(2))); + AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5))); + AddStep("Miss Small Stream", () => SetContents(_ => testStream(7))); + AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true))); + AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true))); + AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true))); } private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index d40484f5ed..fc5fcf2358 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestVariousSliders() { - AddStep("Big Single", () => SetContents(() => testSimpleBig())); - AddStep("Medium Single", () => SetContents(() => testSimpleMedium())); - AddStep("Small Single", () => SetContents(() => testSimpleSmall())); - AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1))); - AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1))); - AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1))); - AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2))); - AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2))); - AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2))); + AddStep("Big Single", () => SetContents(_ => testSimpleBig())); + AddStep("Medium Single", () => SetContents(_ => testSimpleMedium())); + AddStep("Small Single", () => SetContents(_ => testSimpleSmall())); + AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1))); + AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1))); + AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1))); + AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2))); + AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2))); + AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2))); - AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps - AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed())); - AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1))); - AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2))); + AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps + AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed())); + AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1))); + AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2))); - AddStep("Fast Slider", () => SetContents(() => testHighSpeed())); - AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1))); - AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2))); - AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed())); - AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1))); - AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2))); - AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6))); + AddStep("Fast Slider", () => SetContents(_ => testHighSpeed())); + AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1))); + AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2))); + AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed())); + AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1))); + AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2))); + AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6))); - AddStep("Perfect Curve", () => SetContents(() => testPerfect())); - AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1))); - AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2))); + AddStep("Perfect Curve", () => SetContents(_ => testPerfect())); + AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1))); + AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2))); - AddStep("Linear Slider", () => SetContents(() => testLinear())); - AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1))); - AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2))); + AddStep("Linear Slider", () => SetContents(_ => testLinear())); + AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1))); + AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2))); - AddStep("Bezier Slider", () => SetContents(() => testBezier())); - AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1))); - AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2))); + AddStep("Bezier Slider", () => SetContents(_ => testBezier())); + AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1))); + AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2))); - AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping())); - AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1))); - AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2))); + AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping())); + AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1))); + AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2))); - AddStep("Catmull Slider", () => SetContents(() => testCatmull())); - AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1))); - AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2))); + AddStep("Catmull Slider", () => SetContents(_ => testCatmull())); + AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1))); + AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2))); - AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset())); - AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1))); + AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset())); + AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1))); - AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow())); - AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1))); + AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow())); + AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1))); } [Test] diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 0a7ef443b1..b21b7a6f4a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestVariousSpinners(bool autoplay) { string term = autoplay ? "Hit" : "Miss"; - AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay))); - AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay))); - AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay))); + AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay))); + AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay))); + AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay))); } [Test] public void TestSpinningSamplePitchShift() { - AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000))); + AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000))); AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8); AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8); @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestLongSpinner(bool autoplay) { - AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000))); + AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000))); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0)); } @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestSuperShortSpinner(bool autoplay) { - AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200))); + AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200))); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1); } diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs index ff309f278e..f9b8e9a985 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableBarLine.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Bar line", () => SetContents(() => + AddStep("Bar line", () => SetContents(_ => { ScrollingHitObjectContainer hoc; @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning return cont; })); - AddStep("Bar line (major)", () => SetContents(() => + AddStep("Bar line (major)", () => SetContents(_ => { ScrollingHitObjectContainer hoc; diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs index 44646e5fc9..26a4e85fe5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableDrumRoll.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Drum roll", () => SetContents(() => + AddStep("Drum roll", () => SetContents(_ => { var hoc = new ScrollingHitObjectContainer(); @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning return hoc; })); - AddStep("Drum roll (strong)", () => SetContents(() => + AddStep("Drum roll (strong)", () => SetContents(_ => { var hoc = new ScrollingHitObjectContainer(); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs index 9930d97d31..c4ee68206c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableHit.cs @@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) + AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) + AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime()) + AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, })); - AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true)) + AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 8d1aafdcc2..b976735223 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("set beatmap", () => setBeatmap()); - AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear))); - AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle))); - AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai))); - AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); + AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear))); + AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle))); + AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai))); + AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail))); } [Test] public void TestInitialState() { - AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); + AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle)); } @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { AddStep("set beatmap", () => setBeatmap()); - AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); + AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both })); AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear)); AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss })); @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { Beatmap.Value.Track.Start(); - SetContents(() => + SetContents(_ => { var ruleset = new TaikoRuleset(); return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo)); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs index 61ea8b664d..1cba6c9008 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneHitExplosion.cs @@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestNormalHit() { - AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great)))); - AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok)))); - AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss)))); + AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great)))); + AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok)))); + AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss)))); } [TestCase(HitResult.Great)] [TestCase(HitResult.Ok)] public void TestStrongHit(HitResult type) { - AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type)))); + AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type)))); AddStep("visualise second hit", () => this.ChildrenOfType() .ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())))); diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs index 9b36b064bc..055a292fe8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneInputDrum.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [BackgroundDependencyLoader] private void load() { - SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo) + SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo) { RelativeSizeAxes = Axes.Both, Child = new Container diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs index b558709592..419e100296 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs @@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestKiaiHits() { - AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim)))); - AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre)))); + AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim)))); + AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre)))); } private Drawable getContentFor(DrawableTestHit hit) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs index 7b7e2c43d1..f96297a06d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoPlayfield.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning Beatmap.Value.Track.Start(); }); - AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo()) + AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo()) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 14c3599fcd..9882c7bc90 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning public TestSceneTaikoScroller() { - AddStep("Load scroller", () => SetContents(() => + AddStep("Load scroller", () => SetContents(_ => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty()) { Clock = new FramedClock(clock), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 9501026edc..52bedc328d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -29,8 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false); - AddStep("create sprites", () => SetContents( - () => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); + AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); assertSpritesFromSkin(false); } @@ -42,8 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true); - AddStep("create sprites", () => SetContents( - () => createSprite(lookup_name, Anchor.Centre, Vector2.Zero))); + AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.Centre, Vector2.Zero))); assertSpritesFromSkin(true); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs index 14bd62b98a..58c89411c0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorComponentsList.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestToggleEditor() { - AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300) + AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index 245e190b1f..856747ad19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create editor overlay", () => { - SetContents(() => + SetContents(_ => { var ruleset = new OsuRuleset(); var mods = new[] { ruleset.GetAutoplayMod() }; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs index c92e9dcfd5..723e35ed55 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHUDOverlay.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create overlay", () => { - SetContents(() => + SetContents(_ => { hudOverlay = new HUDOverlay(null, Array.Empty()); diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index b04d4f3170..ef44d0df24 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -52,9 +52,7 @@ namespace osu.Game.Tests.Visual private readonly List createdDrawables = new List(); - public void SetContents(Func creationFunction) => SetContents(_ => creationFunction?.Invoke()); - - public void SetContents(Func creationFunction) + protected void SetContents(Func creationFunction) { createdDrawables.Clear(); From e76e540ac11338cb5a83b87c04c835db5c4fb1e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 16:45:25 +0900 Subject: [PATCH 158/165] Fix iOS props Newtonsoft.Json version not matching osu.Game --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index c1d63f3236..cbb6a21fd1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -92,7 +92,7 @@ - + From e0eb0adb0af1aa7b30b875fa7aa8e5df756af96d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 18:32:14 +0900 Subject: [PATCH 159/165] Remove unnecessary bind in `ReplayPlayer` --- osu.Game/Screens/Play/ReplayPlayer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index e440e7d34e..f70c05c2ff 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -34,9 +34,6 @@ namespace osu.Game.Screens.Play protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); - - // todo: move to base class along with Score? - ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo); } protected override Score CreateScore() => createScore(GameplayBeatmap.PlayableBeatmap, Mods.Value); From cde8de154d10e5470dac1f974938e861d2b8db72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 2 Jun 2021 19:11:09 +0900 Subject: [PATCH 160/165] Remove unused test property for now --- osu.Game/Tests/Visual/TestReplayPlayer.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Tests/Visual/TestReplayPlayer.cs b/osu.Game/Tests/Visual/TestReplayPlayer.cs index ac47d186eb..da302d018d 100644 --- a/osu.Game/Tests/Visual/TestReplayPlayer.cs +++ b/osu.Game/Tests/Visual/TestReplayPlayer.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -38,8 +36,6 @@ namespace osu.Game.Tests.Visual public new bool PauseCooldownActive => base.PauseCooldownActive; - public readonly List Results = new List(); - /// /// Instantiate a replay player that renders an autoplay mod. /// @@ -65,11 +61,5 @@ namespace osu.Game.Tests.Visual { PauseOnFocusLost = pauseOnFocusLost; } - - [BackgroundDependencyLoader] - private void load() - { - ScoreProcessor.NewJudgement += r => Results.Add(r); - } } } From d6656047e3e46a16abf8c2647237acb9e3b97e22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 01:58:50 +0900 Subject: [PATCH 161/165] Fix beatmap statistics with value of zero not displaying correctly at song select Closes #13307. --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index b0084735b1..53e30fd9ca 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -186,11 +186,11 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private (float baseValue, float? adjustedValue) value; + private (float baseValue, float? adjustedValue)? value; public (float baseValue, float? adjustedValue) Value { - get => value; + get => value ?? (0, null); set { if (value == this.value) From 66dd7b77055cfe297273148bf308372a025f17a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 14:37:38 +0900 Subject: [PATCH 162/165] Update test logic to allow gameplay to properly continue --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index dd05ce9b7e..0308d74aa4 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -95,11 +96,12 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("set nofail", () => Game.SelectedMods.Value = new[] { new OsuModNoFail() }); + AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } }); AddStep("press enter", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); - AddStep("seek to end", () => player.ChildrenOfType().First().Seek(beatmap().Track.Length)); + AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning); + AddStep("seek to near end", () => player.ChildrenOfType().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000)); AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded); AddStep("attempt to retry", () => results.ChildrenOfType().First().Action()); AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player); From 94701b77cb261963a66106c0b4270a59de6dfc24 Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Thu, 3 Jun 2021 15:44:28 +1000 Subject: [PATCH 163/165] Add TODO for variable clockrate support in catch difficulty calculator --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 7d61be7bb1..83db9216ed 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills // In catch, rate adjustment mods do not only affect the timings of hitobjects, // but also the speed of the player's catcher, which has an impact on difficulty + // TODO: Support variable clockrates caused by mods such as ModTimeRamp + // (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing) var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; } From f51413ead9a0c1afba047526ff9fae183ff1c9db Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Thu, 3 Jun 2021 16:09:37 +1000 Subject: [PATCH 164/165] Refactor to pass clockrate in constructor rather than deriving from mods --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 4 ++-- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 8 +++----- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 5 +++-- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index 59a68245a6..a80f1178b6 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyFreeform protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6340f6c25..290148d14b 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 7f29c4e712..f557a4c754 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyScrolling protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index f6340f6c25..290148d14b 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0]; + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0]; } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f5cce47186..9feaa55051 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f; @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new Skill[] { - new Movement(mods, halfCatcherWidth), + new Movement(mods, halfCatcherWidth, clockRate), }; } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 849af75228..4372ed938c 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -34,17 +33,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills /// private readonly double catcherSpeedMultiplier; - public Movement(Mod[] mods, float halfCatcherWidth) + public Movement(Mod[] mods, float halfCatcherWidth, double clockRate) : base(mods) { HalfCatcherWidth = halfCatcherWidth; - // In catch, rate adjustment mods do not only affect the timings of hitobjects, + // In catch, clockrate adjustments do not only affect the timings of hitobjects, // but also the speed of the player's catcher, which has an impact on difficulty // TODO: Support variable clockrates caused by mods such as ModTimeRamp // (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing) - var rateAdjustMod = mods.FirstOrDefault(m => m is ModRateAdjust); - catcherSpeedMultiplier = (rateAdjustMod as ModRateAdjust)?.SpeedChange.Value ?? 1; + catcherSpeedMultiplier = clockRate; } protected override double StrainValueOf(DifficultyHitObject current) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 8c0b9ed8b7..a7a6677b68 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty // Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required. protected override IEnumerable SortObjects(IEnumerable input) => input; - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns) }; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 75d6786d95..e47f82fb39 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty } } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), new Speed(mods) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 6b3e31c5d5..18d06c069f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Colour(mods), new Rhythm(mods), diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 16c1004f37..e458e66ab7 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -217,7 +217,7 @@ namespace osu.Game.Tests.NonVisual throw new NotImplementedException(); } - protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { throw new NotImplementedException(); } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5780fe39fa..3cc69bd85b 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Difficulty private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { - var skills = CreateSkills(beatmap, mods); + var skills = CreateSkills(beatmap, mods, clockRate); if (!beatmap.HitObjects.Any()) return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); @@ -180,7 +180,8 @@ namespace osu.Game.Rulesets.Difficulty /// /// The whose difficulty will be calculated. /// Mods to calculate difficulty with. + /// Clockrate to calculate difficulty with. /// The s. - protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods); + protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate); } } From be91203c92ba7004f0f03b32878b3a4182092584 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jun 2021 15:35:12 +0900 Subject: [PATCH 165/165] Add nested `PlatformActionContainer` to allow testing of platform actions in visual tests --- osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs index 01dd7a25c8..c7edc0174a 100644 --- a/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs +++ b/osu.Game/Tests/Visual/OsuManualInputManagerTestScene.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Framework.Testing.Input; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -48,7 +49,7 @@ namespace osu.Game.Tests.Visual InputManager = new ManualInputManager { UseParentInput = true, - Child = mainContent + Child = new PlatformActionContainer().WithChild(mainContent) }, new Container {