From c9b96a18294b0143a38407951b15072a7cd8f9ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Nov 2020 15:52:57 +0900 Subject: [PATCH 01/46] Disable spectator streaming connection logic on iOS to avoid startup crash Due to an [issue](https://github.com/dotnet/runtime/issues/35988) at xamarin's end (which *should* have been fixed via [this pr](https://github.com/xamarin/xamarin-macios/pull/8615) but still fails on latest preview release) we can't support this just yet. --- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index cb170ad298..125831035a 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -10,6 +10,7 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -101,6 +102,10 @@ namespace osu.Game.Online.Spectator private async Task connect() { + if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS) + // disabled for now (see https://github.com/dotnet/runtime/issues/35988) + return; + if (connection != null) return; From 1d90f5fc082b636eb4f2a6466deada44bdd16d43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 14:16:51 +0900 Subject: [PATCH 02/46] Revert "Disable spectator streaming connection logic on iOS to avoid startup crash" This reverts commit c9b96a18294b0143a38407951b15072a7cd8f9ff. --- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 125831035a..cb170ad298 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -10,7 +10,6 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -102,10 +101,6 @@ namespace osu.Game.Online.Spectator private async Task connect() { - if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS) - // disabled for now (see https://github.com/dotnet/runtime/issues/35988) - return; - if (connection != null) return; From 5316d4c3696d7939f47b5364286e3e9c0c463dd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 14:16:34 +0900 Subject: [PATCH 03/46] Force using older signalr version for iOS --- osu.iOS/osu.iOS.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1e9a21865d..2c6489a5d3 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -116,5 +116,9 @@ false + + + + From 6c4acb4d11c404602085f5e9a4132da44ff37faf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 14:45:13 +0900 Subject: [PATCH 04/46] Move to props and ignore downgrade warning --- osu.iOS.props | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.iOS.props b/osu.iOS.props index 76c496cd2d..61bb690225 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -73,6 +73,14 @@ + + + NU1605 + + + + + From d1829820e09559791f9436ac6c1c410faf47893c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 14:53:29 +0900 Subject: [PATCH 05/46] Remove local changes from csproj --- osu.iOS/osu.iOS.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 2c6489a5d3..1e9a21865d 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -116,9 +116,5 @@ false - - - - From 7d0a7f170c388e90cf0f4f004fbd260e02499e92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 14:57:47 +0900 Subject: [PATCH 06/46] Avoid overwriting inherited nowarns Co-authored-by: Dan Balasescu --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index 61bb690225..40ecfffcca 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -75,7 +75,7 @@ - NU1605 + $(NoWarn);NU1605 From 8b1dd31bb4b6525ed33973a388423095f87c4b7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 15:20:41 +0900 Subject: [PATCH 07/46] Add gitignore ruile for new msbuild generated editorconfig file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 732b171f69..d122d25054 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,5 @@ inspectcode # BenchmarkDotNet /BenchmarkDotNet.Artifacts + +*.GeneratedMSBuildEditorConfig.editorconfig From 9343f84a6881111986abf1835a1d241db0adda96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 15:21:20 +0900 Subject: [PATCH 08/46] Add IBindableList.GetBoudCopy to banned symbols for now --- CodeAnalysis/BannedSymbols.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index e34626a59e..47839608c9 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -4,5 +4,6 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals( M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. +M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods. From 487a5ecdba11f6f2f9aee7e89ccf752bd598bb1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 15:29:14 +0900 Subject: [PATCH 09/46] Replace all usage of IBindableList.GetBoundCopy --- osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs | 4 ++-- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 4 ++-- .../Timelines/Summary/Parts/ControlPointPart.cs | 4 ++-- .../Timelines/Summary/Parts/GroupVisualisation.cs | 4 ++-- .../Components/Timeline/TimelineControlPointDisplay.cs | 4 ++-- .../Components/Timeline/TimelineControlPointGroup.cs | 4 ++-- osu.Game/Screens/Edit/Timing/ControlPointTable.cs | 4 ++-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 9 ++++----- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs index 4a3dc58604..e4dc261363 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Audio private readonly ControlPointInfo controlPoints; private readonly Dictionary mappings = new Dictionary(); - private IBindableList samplePoints; + private readonly IBindableList samplePoints = new BindableList(); public DrumSampleContainer(ControlPointInfo controlPoints) { @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Audio [BackgroundDependencyLoader] private void load() { - samplePoints = controlPoints.SamplePoints.GetBoundCopy(); + samplePoints.BindTo(controlPoints.SamplePoints); samplePoints.BindCollectionChanged((_, __) => recreateMappings(), true); } diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 34444c2fa5..697ceacf0a 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Dashboard { internal class CurrentlyPlayingDisplay : CompositeDrawable { - private IBindableList playingUsers; + private readonly IBindableList playingUsers = new BindableList(); private FillFlowContainer userFlow; @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Dashboard { base.LoadComplete(); - playingUsers = spectatorStreaming.PlayingUsers.GetBoundCopy(); + playingUsers.BindTo(spectatorStreaming.PlayingUsers); playingUsers.BindCollectionChanged((sender, e) => Schedule(() => { switch (e.Action) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs index 8c0e31c04c..ba3ac9113e 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs @@ -14,13 +14,13 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// public class ControlPointPart : TimelinePart { - private IBindableList controlPointGroups; + private readonly IBindableList controlPointGroups = new BindableList(); protected override void LoadBeatmap(WorkingBeatmap beatmap) { base.LoadBeatmap(beatmap); - controlPointGroups = beatmap.Beatmap.ControlPointInfo.Groups.GetBoundCopy(); + controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs index b9eb53b697..93fe6f9989 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { public readonly ControlPointGroup Group; - private BindableList controlPoints; + private readonly IBindableList controlPoints = new BindableList(); [Resolved] private OsuColour colours { get; set; } @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { base.LoadComplete(); - controlPoints = (BindableList)Group.ControlPoints.GetBoundCopy(); + controlPoints.BindTo(Group.ControlPoints); controlPoints.BindCollectionChanged((_, __) => { if (controlPoints.Count == 0) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs index 3f13e8e5d4..0da1b43201 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public class TimelineControlPointDisplay : TimelinePart { - private IBindableList controlPointGroups; + private readonly IBindableList controlPointGroups = new BindableList(); public TimelineControlPointDisplay() { @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.LoadBeatmap(beatmap); - controlPointGroups = beatmap.Beatmap.ControlPointInfo.Groups.GetBoundCopy(); + controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs index e32616a574..fb69f16792 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public readonly ControlPointGroup Group; - private BindableList controlPoints; + private readonly IBindableList controlPoints = new BindableList(); [Resolved] private OsuColour colours { get; set; } @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.LoadComplete(); - controlPoints = (BindableList)Group.ControlPoints.GetBoundCopy(); + controlPoints.BindTo(Group.ControlPoints); controlPoints.BindCollectionChanged((_, __) => { ClearInternal(); diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 64f9526816..89d3c36250 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Edit.Timing private class ControlGroupAttributes : CompositeDrawable { - private readonly IBindableList controlPoints; + private readonly IBindableList controlPoints = new BindableList(); private readonly FillFlowContainer fill; @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Timing Spacing = new Vector2(2) }; - controlPoints = group.ControlPoints.GetBoundCopy(); + controlPoints.BindTo(group.ControlPoints); } [Resolved] diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index f511382cde..09d861522a 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit.Timing private OsuButton deleteButton; private ControlPointTable table; - private IBindableList controlGroups; + private readonly IBindableList controlPointGroups = new BindableList(); [Resolved] private EditorClock clock { get; set; } @@ -124,11 +124,10 @@ namespace osu.Game.Screens.Edit.Timing selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true); - controlGroups = Beatmap.Value.Beatmap.ControlPointInfo.Groups.GetBoundCopy(); - - controlGroups.BindCollectionChanged((sender, args) => + controlPointGroups.BindTo(Beatmap.Value.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindCollectionChanged((sender, args) => { - table.ControlGroups = controlGroups; + table.ControlGroups = controlPointGroups; changeHandler.SaveState(); }, true); } From ce1743ab283d80fb1868d00d3c7b4169fdf17569 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Nov 2020 15:35:42 +0900 Subject: [PATCH 10/46] Fix missed usage in test scene --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 1d8231cce7..35473ee76c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay private Replay replay; - private IBindableList users; + private readonly IBindableList users = new BindableList(); private TestReplayRecorder recorder; @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay { replay = new Replay(); - users = streamingClient.PlayingUsers.GetBoundCopy(); + users.BindTo(streamingClient.PlayingUsers); users.BindCollectionChanged((obj, args) => { switch (args.Action) From 9a7fcadabc60b0383d72d2933de2df4c38c62e4b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 13:51:46 +0900 Subject: [PATCH 11/46] Use BDL for top-level osu! DHOs --- .../TestSceneSliderPlacementBlueprint.cs | 2 +- .../TestSceneSlider.cs | 18 +++--- .../Sliders/SliderSelectionBlueprint.cs | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 39 ++++-------- .../Objects/Drawables/DrawableOsuHitObject.cs | 22 ++++++- .../Objects/Drawables/DrawableSlider.cs | 60 ++++++++----------- .../Objects/Drawables/DrawableSliderHead.cs | 5 +- .../Objects/Drawables/DrawableSliderRepeat.cs | 25 +++----- .../Objects/Drawables/DrawableSliderTail.cs | 23 +++---- .../Objects/Drawables/DrawableSliderTick.cs | 17 +++--- .../Objects/Drawables/DrawableSpinner.cs | 48 +++++++-------- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 2 +- .../Skinning/LegacyNewStyleSpinner.cs | 3 +- .../Objects/Drawables/DrawableHitObject.cs | 4 +- 14 files changed, 118 insertions(+), 152 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 49d7d9249c..a452f93676 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -295,7 +295,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertControlPointPosition(int index, Vector2 position) => AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1)); - private Slider getSlider() => HitObjectContainer.Count > 0 ? (Slider)((DrawableSlider)HitObjectContainer[0]).HitObject : null; + private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null; protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject); protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint(); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index c9e112f76d..c400e2f2ea 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -112,10 +112,10 @@ namespace osu.Game.Rulesets.Osu.Tests new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE }, }); - AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); - AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples)); - AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples)); - AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); + AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle)); + AddAssert("tick samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertTickSamples)); + AddAssert("repeat samples updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertSamples)); + AddAssert("tail has no samples", () => slider.HitObject.TailCircle.Samples.Count == 0); static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests slider = (DrawableSlider)createSlider(repeats: 1); for (int i = 0; i < 2; i++) - ((Slider)slider.HitObject).NodeSamples.Add(new List { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } }); + slider.HitObject.NodeSamples.Add(new List { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } }); Add(slider); }); @@ -147,10 +147,10 @@ namespace osu.Game.Rulesets.Osu.Tests new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE }, }); - AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); - AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples)); - AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples)); - AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); + AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle)); + AddAssert("tick samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertTickSamples)); + AddAssert("repeat samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertSamples)); + AddAssert("tail has no samples", () => slider.HitObject.TailCircle.Samples.Count == 0); static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index d3fb5defae..f851c7bfc9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override void OnSelected() { - AddInternal(ControlPointVisualiser = new PathControlPointVisualiser((Slider)slider.HitObject, true) + AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(slider.HitObject, true) { RemoveControlPointsRequested = removeControlPoints }); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index b5ac26c824..0c26e2b52f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -21,28 +20,25 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { - public ApproachCircle ApproachCircle { get; } - - private readonly IBindable positionBindable = new Bindable(); - private readonly IBindable stackHeightBindable = new Bindable(); - private readonly IBindable scaleBindable = new BindableFloat(); - public OsuAction? HitAction => HitArea.HitAction; - - public readonly HitReceptor HitArea; - public readonly SkinnableDrawable CirclePiece; - private readonly Container scaleContainer; - protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; + public ApproachCircle ApproachCircle { get; private set; } + public HitReceptor HitArea { get; private set; } + public SkinnableDrawable CirclePiece { get; private set; } + + private Container scaleContainer; private InputManager inputManager; public DrawableHitCircle(HitCircle h) : base(h) { - Origin = Anchor.Centre; + } - Position = HitObject.StackedPosition; + [BackgroundDependencyLoader] + private void load() + { + Origin = Anchor.Centre; InternalChildren = new Drawable[] { @@ -75,19 +71,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; Size = HitArea.DrawSize; - } - - [BackgroundDependencyLoader] - private void load() - { - positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); - - positionBindable.BindTo(HitObject.PositionBindable); - stackHeightBindable.BindTo(HitObject.StackHeightBindable); - scaleBindable.BindTo(HitObject.ScaleBindable); + PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); + StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 45c664ba3b..c83bdf17d5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -2,18 +2,24 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Osu.UI; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuHitObject : DrawableHitObject { - private readonly ShakeContainer shakeContainer; + public readonly IBindable PositionBindable = new Bindable(); + public readonly IBindable StackHeightBindable = new Bindable(); + public readonly IBindable ScaleBindable = new BindableFloat(); + public readonly IBindable IndexInCurrentComboBindable = new Bindable(); // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects. public override bool HandlePositionalInput => true; @@ -26,16 +32,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public Func CheckHittable; + private ShakeContainer shakeContainer; + protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { + } + + [BackgroundDependencyLoader] + private void load() + { + Alpha = 0; + base.AddInternal(shakeContainer = new ShakeContainer { ShakeDuration = 30, RelativeSizeAxes = Axes.Both }); - Alpha = 0; + IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable); + PositionBindable.BindTo(HitObject.PositionBindable); + StackHeightBindable.BindTo(HitObject.StackHeightBindable); + ScaleBindable.BindTo(HitObject.ScaleBindable); } // Forward all internal management to shakeContainer. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index b00d12983d..b743d2e4d0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -20,62 +20,50 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { + public new Slider HitObject => (Slider)base.HitObject; + public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; - public readonly SliderBall Ball; - public readonly SkinnableDrawable Body; + public SliderBall Ball { get; private set; } + public SkinnableDrawable Body { get; private set; } public override bool DisplayResult => false; private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody; - private readonly Container headContainer; - private readonly Container tailContainer; - private readonly Container tickContainer; - private readonly Container repeatContainer; - - private readonly Slider slider; - - private readonly IBindable positionBindable = new Bindable(); - private readonly IBindable stackHeightBindable = new Bindable(); - private readonly IBindable scaleBindable = new BindableFloat(); + private Container headContainer; + private Container tailContainer; + private Container tickContainer; + private Container repeatContainer; public DrawableSlider(Slider s) : base(s) { - slider = s; - - Position = s.StackedPosition; + } + [BackgroundDependencyLoader] + private void load() + { InternalChildren = new Drawable[] { Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling), tailContainer = new Container { RelativeSizeAxes = Axes.Both }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, - Ball = new SliderBall(s, this) + Ball = new SliderBall(HitObject, this) { GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, - Scale = new Vector2(s.Scale), AlwaysPresent = true, Alpha = 0 }, headContainer = new Container { RelativeSizeAxes = Axes.Both }, }; - } - [BackgroundDependencyLoader] - private void load() - { - positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - scaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue)); - - positionBindable.BindTo(HitObject.PositionBindable); - stackHeightBindable.BindTo(HitObject.StackHeightBindable); - scaleBindable.BindTo(HitObject.ScaleBindable); + PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); + StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); + ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue), true); AccentColour.BindValueChanged(colour => { @@ -162,20 +150,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables switch (hitObject) { case SliderTailCircle tail: - return new DrawableSliderTail(slider, tail); + return new DrawableSliderTail(tail); case SliderHeadCircle head: - return new DrawableSliderHead(slider, head) + return new DrawableSliderHead(HitObject, head) { OnShake = Shake, CheckHittable = (d, t) => CheckHittable?.Invoke(d, t) ?? true }; case SliderTick tick: - return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position }; + return new DrawableSliderTick(tick) { Position = tick.Position - HitObject.Position }; case SliderRepeat repeat: - return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - slider.Position }; + return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - HitObject.Position }; } return base.CreateNestedHitObject(hitObject); @@ -200,14 +188,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // keep the sliding sample playing at the current tracking position slidingSample.Balance.Value = CalculateSamplePlaybackBalance(Ball.X / OsuPlayfield.BASE_SIZE.X); - double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); + double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); sliderBody?.UpdateProgress(completionProgress); foreach (DrawableHitObject hitObject in NestedHitObjects) { - if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(slider.Path.PositionAt(sliderBody?.SnakedStart ?? 0), slider.Path.PositionAt(sliderBody?.SnakedEnd ?? 0)); + if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(sliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(sliderBody?.SnakedEnd ?? 0)); if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking; } @@ -239,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { - if (userTriggered || Time.Current < slider.EndTime) + if (userTriggered || Time.Current < HitObject.EndTime) return; ApplyResult(r => r.Type = r.Judgement.MaxResult); @@ -260,7 +248,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Ball.FadeIn(); Ball.ScaleTo(HitObject.Scale); - using (BeginDelayedSequence(slider.Duration, true)) + using (BeginDelayedSequence(HitObject.Duration, true)) { const float fade_out_time = 450; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 04f563eeec..49ed9f12e3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -5,13 +5,11 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; -using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSliderHead : DrawableHitCircle { - private readonly IBindable positionBindable = new Bindable(); private readonly IBindable pathVersion = new Bindable(); protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle; @@ -27,10 +25,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load() { - positionBindable.BindTo(HitObject.PositionBindable); pathVersion.BindTo(slider.Path.Version); - positionBindable.BindValueChanged(_ => updatePosition()); + PositionBindable.BindValueChanged(_ => updatePosition()); pathVersion.BindValueChanged(_ => updatePosition(), true); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 2a88f11f69..b57bb8dbbf 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; @@ -22,9 +21,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private double animDuration; - private readonly Drawable scaleContainer; - - public readonly Drawable CirclePiece; + public Drawable CirclePiece { get; private set; } + private Drawable scaleContainer; + private ReverseArrowPiece arrow; public override bool DisplayResult => false; @@ -33,10 +32,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { this.sliderRepeat = sliderRepeat; this.drawableSlider = drawableSlider; + } - Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); - + [BackgroundDependencyLoader] + private void load() + { Origin = Anchor.Centre; + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); InternalChild = scaleContainer = new Container { @@ -50,15 +52,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables arrow = new ReverseArrowPiece(), } }; - } - private readonly IBindable scaleBindable = new BindableFloat(); - - [BackgroundDependencyLoader] - private void load() - { - scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); - scaleBindable.BindTo(HitObject.ScaleBindable); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -100,8 +95,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private bool hasRotation; - private readonly ReverseArrowPiece arrow; - public void UpdateSnakingPosition(Vector2 start, Vector2 end) { // When the repeat is hit, the arrow should fade out on spot rather than following the slider diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index f5bcecccdf..7e30f4e132 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; @@ -23,18 +22,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking { get; set; } - private readonly IBindable scaleBindable = new BindableFloat(); + private SkinnableDrawable circlePiece; + private Container scaleContainer; - private readonly SkinnableDrawable circlePiece; - - private readonly Container scaleContainer; - - public DrawableSliderTail(Slider slider, SliderTailCircle tailCircle) + public DrawableSliderTail(SliderTailCircle tailCircle) : base(tailCircle) { this.tailCircle = tailCircle; - Origin = Anchor.Centre; + } + [BackgroundDependencyLoader] + private void load() + { + Origin = Anchor.Centre; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); InternalChildren = new Drawable[] @@ -51,13 +51,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } }, }; - } - [BackgroundDependencyLoader] - private void load() - { - scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); - scaleBindable.BindTo(HitObject.ScaleBindable); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); } protected override void UpdateInitialTransforms() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 9b68b446a4..81d12f3432 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -23,10 +22,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool DisplayResult => false; - private readonly SkinnableDrawable scaleContainer; + private SkinnableDrawable scaleContainer; public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) + { + } + + [BackgroundDependencyLoader] + private void load() { Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Origin = Anchor.Centre; @@ -49,15 +53,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, }; - } - private readonly IBindable scaleBindable = new BindableFloat(); - - [BackgroundDependencyLoader] - private void load() - { - scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); - scaleBindable.BindTo(HitObject.ScaleBindable); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 936bfaeb86..50ea45c378 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -16,34 +16,33 @@ using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSpinner : DrawableOsuHitObject { - protected readonly Spinner Spinner; + public new Spinner HitObject => (Spinner)base.HitObject; - private readonly Container ticks; + public SpinnerRotationTracker RotationTracker { get; private set; } + public SpinnerSpmCounter SpmCounter { get; private set; } - public readonly SpinnerRotationTracker RotationTracker; - public readonly SpinnerSpmCounter SpmCounter; - private readonly SpinnerBonusDisplay bonusDisplay; - - private readonly IBindable positionBindable = new Bindable(); + private Container ticks; + private SpinnerBonusDisplay bonusDisplay; + private Bindable isSpinning; private bool spinnerFrequencyModulate; public DrawableSpinner(Spinner s) : base(s) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) { Origin = Anchor.Centre; - Position = s.Position; - RelativeSizeAxes = Axes.Both; - Spinner = s; - InternalChildren = new Drawable[] { ticks = new Container(), @@ -55,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Children = new Drawable[] { new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()), - RotationTracker = new SpinnerRotationTracker(Spinner) + RotationTracker = new SpinnerRotationTracker(HitObject) } }, SpmCounter = new SpinnerSpmCounter @@ -72,9 +71,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Y = -120, } }; - } - private Bindable isSpinning; + PositionBindable.BindValueChanged(pos => Position = pos.NewValue, true); + } protected override void LoadComplete() { @@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateStateTransforms(state); - using (BeginDelayedSequence(Spinner.Duration, true)) + using (BeginDelayedSequence(HitObject.Duration, true)) this.FadeOut(160); // skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback. @@ -173,13 +172,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return base.CreateNestedHitObject(hitObject); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - positionBindable.BindValueChanged(pos => Position = pos.NewValue); - positionBindable.BindTo(HitObject.PositionBindable); - } - protected override void ApplySkin(ISkinSource skin, bool allowFallback) { base.ApplySkin(skin, allowFallback); @@ -193,12 +185,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { get { - if (Spinner.SpinsRequired == 0) + if (HitObject.SpinsRequired == 0) // some spinners are so short they can't require an integer spin count. // these become implicitly hit. return 1; - return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / Spinner.SpinsRequired, 0, 1); + return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / HitObject.SpinsRequired, 0, 1); } } @@ -208,7 +200,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RotationTracker.Complete.Value = Progress >= 1; - if (userTriggered || Time.Current < Spinner.EndTime) + if (userTriggered || Time.Current < HitObject.EndTime) return; // Trigger a miss result for remaining ticks to avoid infinite gameplay. @@ -223,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables r.Type = HitResult.Ok; else if (Progress > .75) r.Type = HitResult.Meh; - else if (Time.Current >= Spinner.EndTime) + else if (Time.Current >= HitObject.EndTime) r.Type = r.Judgement.MinResult; }); } @@ -275,7 +267,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { tick.TriggerResult(true); if (tick is DrawableSpinnerBonusTick) - bonusDisplay.SetBonusCount(spins - Spinner.SpinsRequired); + bonusDisplay.SetBonusCount(spins - HitObject.SpinsRequired); } wholeSpins++; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index e855317544..17a734f0f4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load(OsuColour colours, DrawableHitObject drawableHitObject) { drawableSpinner = (DrawableSpinner)drawableHitObject; - spinner = (Spinner)drawableSpinner.HitObject; + spinner = drawableSpinner.HitObject; normalColour = colours.BlueDark; completeColour = colours.YellowLight; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 56b5571ce1..018dc78ddb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Skinning if (!(drawableHitObject is DrawableSpinner)) return; - var spinner = (Spinner)drawableSpinner.HitObject; + var spinner = drawableSpinner.HitObject; using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) this.FadeOut(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1ef6c8c207..cd31e468af 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -125,14 +125,14 @@ namespace osu.Game.Rulesets.Objects.Drawables Result = CreateResult(judgement); if (Result == null) throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); - - LoadSamples(); } protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); + LoadSamples(); + HitObject.DefaultsApplied += onDefaultsApplied; startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy(); From 400fcedf0ff3e7a7de3c73731320124cca8484d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 14:40:48 +0900 Subject: [PATCH 12/46] Remove stored hitobject references from skinnable components --- .../Objects/Drawables/DrawableSlider.cs | 6 ++++- .../Objects/Drawables/DrawableSpinner.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 27 +++++++++---------- .../Drawables/Pieces/MainCirclePiece.cs | 4 +-- .../Drawables/Pieces/PlaySliderBody.cs | 13 +++------ .../Objects/Drawables/Pieces/SliderBall.cs | 12 ++++----- .../Drawables/Pieces/SnakingSliderBody.cs | 24 ++++++++++------- .../Pieces/SpinnerRotationTracker.cs | 10 +++---- .../Skinning/LegacyMainCirclePiece.cs | 5 ++-- .../Skinning/LegacyNewStyleSpinner.cs | 16 +++++------ .../Skinning/LegacyOldStyleSpinner.cs | 10 +++---- .../Skinning/LegacySliderBall.cs | 3 +-- 13 files changed, 65 insertions(+), 69 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index b743d2e4d0..8008d87c6d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody; + public readonly IBindable PathVersion = new Bindable(); + private Container headContainer; private Container tailContainer; private Container tickContainer; @@ -51,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer = new Container { RelativeSizeAxes = Axes.Both }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, - Ball = new SliderBall(HitObject, this) + Ball = new SliderBall(this) { GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, @@ -61,6 +63,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables headContainer = new Container { RelativeSizeAxes = Axes.Both }, }; + PathVersion.BindTo(HitObject.Path.Version); + PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true); ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue), true); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 50ea45c378..8dd63018e2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Children = new Drawable[] { new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()), - RotationTracker = new SpinnerRotationTracker(HitObject) + RotationTracker = new SpinnerRotationTracker(this) } }, SpmCounter = new SpinnerSpmCounter diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index e95cdc7ee3..c455c66e8d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Origin = Anchor.Centre, Texture = textures.Get(@"Gameplay/osu/disc"), }, - new TrianglesPiece((int)drawableHitObject.HitObject.StartTime) + new TrianglesPiece(drawableHitObject.GetHashCode()) { RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 17a734f0f4..4c682d96ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { private DrawableSpinner drawableSpinner; - private Spinner spinner; - private const float initial_scale = 1.3f; private const float idle_alpha = 0.2f; private const float tracking_alpha = 0.4f; @@ -52,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load(OsuColour colours, DrawableHitObject drawableHitObject) { drawableSpinner = (DrawableSpinner)drawableHitObject; - spinner = drawableSpinner.HitObject; normalColour = colours.BlueDark; completeColour = colours.YellowLight; @@ -130,18 +127,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (!(drawableHitObject is DrawableSpinner)) return; - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) { this.ScaleTo(initial_scale); this.RotateTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { // constant ambient rotation to give the spinner "spinning" character. - this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); + this.RotateTo((float)(25 * drawableSpinner.HitObject.Duration / 2000), drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration); } - using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration + drawableHitObject.Result.TimeOffset, true)) { switch (state) { @@ -157,26 +154,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) { centre.ScaleTo(0); mainContainer.ScaleTo(0); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { - centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); - mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); + centre.ScaleTo(0.3f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); + mainContainer.ScaleTo(0.2f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); - using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) { - centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); - mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); + centre.ScaleTo(0.5f, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); + mainContainer.ScaleTo(1, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); } } } // transforms we have from completing the spinner will be rolled back, so reapply immediately. - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) updateComplete(state == ArmedState.Hit, 0); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs index cb3787a493..d2f4b71f19 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; + var drawableOsuObject = (DrawableOsuHitObject)drawableObject; state.BindTo(drawableObject.State); state.BindValueChanged(updateState, true); @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces circle.Colour = colour.NewValue; }, true); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs index cedf2f6e09..29dff53f54 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs @@ -17,23 +17,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private IBindable pathVersion; private IBindable accentColour; - [Resolved] - private DrawableHitObject drawableObject { get; set; } - [Resolved(CanBeNull = true)] private OsuRulesetConfigManager config { get; set; } - private Slider slider; - [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource skin, DrawableHitObject drawableObject) { - slider = (Slider)drawableObject.HitObject; + var drawableSlider = (DrawableSlider)drawableObject; - scaleBindable = slider.ScaleBindable.GetBoundCopy(); + scaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - pathVersion = slider.Path.Version.GetBoundCopy(); + pathVersion = drawableSlider.PathVersion.GetBoundCopy(); pathVersion.BindValueChanged(_ => Refresh()); accentColour = drawableObject.AccentColour.GetBoundCopy(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 07dc6021c9..c5bf790377 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -30,15 +30,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set => ball.Colour = value; } - private readonly Slider slider; private readonly Drawable followCircle; private readonly DrawableSlider drawableSlider; private readonly Drawable ball; - public SliderBall(Slider slider, DrawableSlider drawableSlider = null) + public SliderBall(DrawableSlider drawableSlider) { this.drawableSlider = drawableSlider; - this.slider = slider; Origin = Anchor.Centre; @@ -133,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (headCircleHitAction == null) timeToAcceptAnyKeyAfter = null; - var actions = drawableSlider?.OsuActionInputManager?.PressedActions; + var actions = drawableSlider.OsuActionInputManager?.PressedActions; // if the head circle was hit with a specific key, tracking should only occur while that key is pressed. if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null) @@ -147,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Tracking = // in valid time range - Time.Current >= slider.StartTime && Time.Current < slider.EndTime && + Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime && // in valid position range lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && // valid action @@ -172,9 +170,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - var newPos = slider.CurvePositionAt(completionProgress); + var newPos = drawableSlider.HitObject.CurvePositionAt(completionProgress); - var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f); + var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f); if (diff == Vector2.Zero) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index e24fa865ad..2fefc2e08e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -51,27 +51,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// private Vector2 snakedPathOffset; - private Slider slider; + private DrawableSlider drawableSlider; [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - slider = (Slider)drawableObject.HitObject; + drawableSlider = (DrawableSlider)drawableObject; Refresh(); } public void UpdateProgress(double completionProgress) { - var span = slider.SpanAt(completionProgress); - var spanProgress = slider.ProgressAt(completionProgress); + if (drawableSlider == null) + return; + + var span = drawableSlider.HitObject.SpanAt(completionProgress); + var spanProgress = drawableSlider.HitObject.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; + double end = SnakingIn.Value ? Math.Clamp((Time.Current - (drawableSlider.HitObject.StartTime - drawableSlider.HitObject.TimePreempt)) / (drawableSlider.HitObject.TimePreempt / 3), 0, 1) : 1; - if (span >= slider.SpanCount() - 1) + if (span >= drawableSlider.HitObject.SpanCount() - 1) { - if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) + if (Math.Min(span, drawableSlider.HitObject.SpanCount() - 1) % 2 == 1) { start = 0; end = SnakingOut.Value ? spanProgress : 1; @@ -87,8 +90,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void Refresh() { + if (drawableSlider == null) + return; + // Generate the entire curve - slider.Path.GetPathToProgress(CurrentCurve, 0, 1); + drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, 0, 1); SetVertices(CurrentCurve); // Force the body to be the final path size to avoid excessive autosize computations @@ -132,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces SnakedStart = p0; SnakedEnd = p1; - slider.Path.GetPathToProgress(CurrentCurve, p0, p1); + drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, p0, p1); SetVertices(CurrentCurve); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs index 05ed38d241..910899c307 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SpinnerRotationTracker : CircularContainer { - private readonly Spinner spinner; - public override bool IsPresent => true; // handle input when hidden - public SpinnerRotationTracker(Spinner s) + private readonly DrawableSpinner drawableSpinner; + + public SpinnerRotationTracker(DrawableSpinner drawableSpinner) { - spinner = s; + this.drawableSpinner = drawableSpinner; RelativeSizeAxes = Axes.Both; } @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// /// Whether currently in the correct time range to allow spinning. /// - private bool isSpinnableTime => spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; + private bool isSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current; protected override bool OnMouseMove(MouseMoveEvent e) { diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 382d6e53cc..418bf124ab 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; 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.Skinning; using osuTK; using osuTK.Graphics; @@ -47,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; + var drawableOsuObject = (DrawableOsuHitObject)drawableObject; bool allowFallback = false; @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Skinning state.BindTo(drawableObject.State); accentColour.BindTo(drawableObject.AccentColour); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); + indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); Texture getTextureWithFallback(string name) { diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 018dc78ddb..f07b1038fb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -75,23 +75,21 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner)) + if (!(drawableHitObject is DrawableSpinner spinner)) return; - var spinner = drawableSpinner.HitObject; - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) { fixedMiddle.FadeColour(Color4.White); - using (BeginDelayedSequence(spinner.TimePreempt, true)) - fixedMiddle.FadeColour(Color4.Red, spinner.Duration); + using (BeginDelayedSequence(spinner.HitObject.TimePreempt, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.HitObject.Duration); } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 7b0d7acbbc..5fcd8e06b1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -94,16 +94,14 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner)) + if (!(drawableHitObject is DrawableSpinner spinner)) return; - var spinner = drawableSpinner.HitObject; - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs index 25ab96445a..836069013d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK.Graphics; @@ -26,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } [BackgroundDependencyLoader] - private void load(ISkinSource skin, DrawableHitObject drawableObject) + private void load(ISkinSource skin) { var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; From 9ac822beed8432bb5cb00ce9b4fe6aac419bf9a7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 14:49:15 +0900 Subject: [PATCH 13/46] Remove AccentColour binding from judgement lighting --- .../Objects/Drawables/DrawableOsuJudgement.cs | 22 ++------- .../Objects/Drawables/SkinnableLighting.cs | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 49535e7fff..98898ce1b4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -2,22 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osuTK; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Skinning; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuJudgement : DrawableJudgement { - protected SkinnableSprite Lighting; - - private Bindable lightingColour; + protected SkinnableLighting Lighting { get; private set; } [Resolved] private OsuConfigManager config { get; set; } @@ -34,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AddInternal(Lighting = new SkinnableSprite("lighting") + AddInternal(Lighting = new SkinnableLighting { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -59,19 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.PrepareForUse(); - lightingColour?.UnbindAll(); - Lighting.ResetAnimation(); - - if (JudgedObject != null) - { - lightingColour = JudgedObject.AccentColour.GetBoundCopy(); - lightingColour.BindValueChanged(colour => Lighting.Colour = Result.IsHit ? colour.NewValue : Color4.Transparent, true); - } - else - { - Lighting.Colour = Color4.White; - } + Lighting.SetColourFrom(JudgedObject, Result); } private double fadeOutDelay; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs new file mode 100644 index 0000000000..02dc770285 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs @@ -0,0 +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.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Skinning; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class SkinnableLighting : SkinnableSprite + { + private DrawableHitObject targetObject; + private JudgementResult targetResult; + + public SkinnableLighting() + : base("lighting") + { + } + + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + updateColour(); + } + + /// + /// Updates the lighting colour from a given hitobject and result. + /// + /// The that's been judged. + /// The that was judged with. + public void SetColourFrom(DrawableHitObject targetObject, JudgementResult targetResult) + { + this.targetObject = targetObject; + this.targetResult = targetResult; + + updateColour(); + } + + private void updateColour() + { + if (targetObject == null || targetResult == null) + Colour = Color4.White; + else + Colour = targetResult.IsHit ? targetObject.AccentColour.Value : Color4.Transparent; + } + } +} From 9c1c9945af19ea710a995c4febc931813d0be54a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 15:01:45 +0900 Subject: [PATCH 14/46] Make FollowPointRenderer use hitobject models --- .../TestSceneFollowPoints.cs | 10 ++--- .../Connections/FollowPointConnection.cs | 44 +++++++++---------- .../Connections/FollowPointRenderer.cs | 12 ++--- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 5 ++- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs index 87da7ef417..6c077eb214 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests } hitObjectContainer.Add(drawableObject); - followPointRenderer.AddFollowPoints(drawableObject); + followPointRenderer.AddFollowPoints(objects[i]); } }); } @@ -180,10 +180,10 @@ namespace osu.Game.Rulesets.Osu.Tests { AddStep("remove hitobject", () => { - var drawableObject = getFunc?.Invoke(); + var drawableObject = getFunc.Invoke(); hitObjectContainer.Remove(drawableObject); - followPointRenderer.RemoveFollowPoints(drawableObject); + followPointRenderer.RemoveFollowPoints(drawableObject.HitObject); }); } @@ -215,10 +215,10 @@ namespace osu.Game.Rulesets.Osu.Tests DrawableOsuHitObject expectedStart = getObject(i); DrawableOsuHitObject expectedEnd = i < hitObjectContainer.Count - 1 ? getObject(i + 1) : null; - if (getGroup(i).Start != expectedStart) + if (getGroup(i).Start != expectedStart.HitObject) throw new AssertionException($"Object {i} expected to be the start of group {i}."); - if (getGroup(i).End != expectedEnd) + if (getGroup(i).End != expectedEnd?.HitObject) throw new AssertionException($"Object {(expectedEnd == null ? "null" : i.ToString())} expected to be the end of group {i}."); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 2c41e6b0e9..3a9e19b361 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -31,19 +32,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections /// The which s will exit from. /// [NotNull] - public readonly DrawableOsuHitObject Start; + public readonly OsuHitObject Start; /// /// Creates a new . /// /// The which s will exit from. - public FollowPointConnection([NotNull] DrawableOsuHitObject start) + public FollowPointConnection([NotNull] OsuHitObject start) { Start = start; RelativeSizeAxes = Axes.Both; - StartTime.BindTo(Start.HitObject.StartTimeBindable); + StartTime.BindTo(start.StartTimeBindable); } protected override void LoadComplete() @@ -52,13 +53,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections bindEvents(Start); } - private DrawableOsuHitObject end; + private OsuHitObject end; /// /// The which s will enter. /// [CanBeNull] - public DrawableOsuHitObject End + public OsuHitObject End { get => end; set @@ -75,10 +76,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } } - private void bindEvents(DrawableOsuHitObject drawableObject) + private void bindEvents(OsuHitObject obj) { - drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh()); - drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh(); + obj.PositionBindable.BindValueChanged(_ => scheduleRefresh()); + obj.DefaultsApplied += _ => scheduleRefresh(); } private void scheduleRefresh() @@ -88,23 +89,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections private void refresh() { - OsuHitObject osuStart = Start.HitObject; - double startTime = osuStart.GetEndTime(); + double startTime = Start.GetEndTime(); LifetimeStart = startTime; - OsuHitObject osuEnd = End?.HitObject; - - if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner) + if (End == null || End.NewCombo || Start is Spinner || End is Spinner) { // ensure we always set a lifetime for full LifetimeManagementContainer benefits LifetimeEnd = LifetimeStart; return; } - Vector2 startPosition = osuStart.StackedEndPosition; - Vector2 endPosition = osuEnd.StackedPosition; - double endTime = osuEnd.StartTime; + Vector2 startPosition = Start.StackedEndPosition; + Vector2 endPosition = End.StackedPosition; + double endTime = End.StartTime; Vector2 distanceVector = endPosition - startPosition; int distance = (int)distanceVector.Length; @@ -130,10 +128,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections AddInternal(fp = new FollowPoint()); + Debug.Assert(End != null); + fp.Position = pointStartPosition; fp.Rotation = rotation; fp.Alpha = 0; - fp.Scale = new Vector2(1.5f * osuEnd.Scale); + fp.Scale = new Vector2(1.5f * End.Scale); firstTransformStartTime ??= fadeInTime; @@ -141,12 +141,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections using (fp.BeginAbsoluteSequence(fadeInTime)) { - fp.FadeIn(osuEnd.TimeFadeIn); - fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out); - fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out); - fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn); + fp.FadeIn(End.TimeFadeIn); + fp.ScaleTo(End.Scale, End.TimeFadeIn, Easing.Out); + fp.MoveTo(pointEndPosition, End.TimeFadeIn, Easing.Out); + fp.Delay(fadeOutTime - fadeInTime).FadeOut(End.TimeFadeIn); - finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn; + finalTransformEndTime = fadeOutTime + End.TimeFadeIn; } point++; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 11571ea761..be1392d7c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -24,19 +24,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections public override bool RemoveCompletedTransforms => false; /// - /// Adds the s around a . + /// Adds the s around an . /// This includes s leading into , and s exiting . /// - /// The to add s for. - public void AddFollowPoints(DrawableOsuHitObject hitObject) + /// The to add s for. + public void AddFollowPoints(OsuHitObject hitObject) => addConnection(new FollowPointConnection(hitObject).With(g => g.StartTime.BindValueChanged(_ => onStartTimeChanged(g)))); /// - /// Removes the s around a . + /// Removes the s around an . /// This includes s leading into , and s exiting . /// - /// The to remove s for. - public void RemoveFollowPoints(DrawableOsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject)); + /// The to remove s for. + public void RemoveFollowPoints(OsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject)); /// /// Adds a to this . diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 50727d590a..19502369c4 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -20,6 +20,7 @@ using osu.Game.Skinning; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; osuHitObject.CheckHittable = hitPolicy.IsHittable; - followPoints.AddFollowPoints(osuHitObject); + followPoints.AddFollowPoints((OsuHitObject)h.HitObject); } public override bool Remove(DrawableHitObject h) @@ -118,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.UI bool result = base.Remove(h); if (result) - followPoints.RemoveFollowPoints((DrawableOsuHitObject)h); + followPoints.RemoveFollowPoints((OsuHitObject)h.HitObject); return result; } From 4457e363d39bfa4f8a6f954ab9dbb280b96dcdc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:18:52 +0900 Subject: [PATCH 15/46] Bring back local variables to reduce two-level-deep variable access --- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 26 ++++++++++--------- .../Drawables/Pieces/SnakingSliderBody.cs | 12 +++++---- .../Skinning/LegacyNewStyleSpinner.cs | 17 +++++++----- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 4c682d96ce..731852c221 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -127,18 +127,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (!(drawableHitObject is DrawableSpinner)) return; - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + Spinner spinner = drawableSpinner.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { this.ScaleTo(initial_scale); this.RotateTo(0); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { // constant ambient rotation to give the spinner "spinning" character. - this.RotateTo((float)(25 * drawableSpinner.HitObject.Duration / 2000), drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration); + this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); } - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt + drawableSpinner.HitObject.Duration + drawableHitObject.Result.TimeOffset, true)) + using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true)) { switch (state) { @@ -154,26 +156,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { centre.ScaleTo(0); mainContainer.ScaleTo(0); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { - centre.ScaleTo(0.3f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); - mainContainer.ScaleTo(0.2f, drawableSpinner.HitObject.TimePreempt / 4, Easing.OutQuint); + centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); + mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); - using (BeginDelayedSequence(drawableSpinner.HitObject.TimePreempt / 2, true)) + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { - centre.ScaleTo(0.5f, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); - mainContainer.ScaleTo(1, drawableSpinner.HitObject.TimePreempt / 2, Easing.OutQuint); + centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); + mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); } } } // transforms we have from completing the spinner will be rolled back, so reapply immediately. - using (BeginAbsoluteSequence(drawableSpinner.HitObject.StartTime - drawableSpinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) updateComplete(state == ArmedState.Hit, 0); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs index 2fefc2e08e..e63f25b7bc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs @@ -66,15 +66,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (drawableSlider == null) return; - var span = drawableSlider.HitObject.SpanAt(completionProgress); - var spanProgress = drawableSlider.HitObject.ProgressAt(completionProgress); + Slider slider = drawableSlider.HitObject; + + var span = slider.SpanAt(completionProgress); + var spanProgress = slider.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn.Value ? Math.Clamp((Time.Current - (drawableSlider.HitObject.StartTime - drawableSlider.HitObject.TimePreempt)) / (drawableSlider.HitObject.TimePreempt / 3), 0, 1) : 1; + double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; - if (span >= drawableSlider.HitObject.SpanCount() - 1) + if (span >= slider.SpanCount() - 1) { - if (Math.Min(span, drawableSlider.HitObject.SpanCount() - 1) % 2 == 1) + if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; end = SnakingOut.Value ? spanProgress : 1; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index f07b1038fb..b65e5a784c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -75,21 +76,23 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner spinner)) + if (!(drawableHitObject is DrawableSpinner d)) return; - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + Spinner spinner = d.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.TimeFadeIn / 2); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) { fixedMiddle.FadeColour(Color4.White); - using (BeginDelayedSequence(spinner.HitObject.TimePreempt, true)) - fixedMiddle.FadeColour(Color4.Red, spinner.HitObject.Duration); + using (BeginDelayedSequence(spinner.TimePreempt, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.Duration); } } From e2d028908abe2271e1faeb74b9c6f84384ce049c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:25:20 +0900 Subject: [PATCH 16/46] Fix one more case of local variable preference --- .../Skinning/LegacyOldStyleSpinner.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 5fcd8e06b1..1954ff6e38 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; @@ -94,14 +95,16 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner spinner)) + if (!(drawableHitObject is DrawableSpinner d)) return; - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt, true)) + Spinner spinner = d.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) this.FadeOut(); - using (BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.HitObject.TimeFadeIn / 2); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.TimeFadeIn / 2); } protected override void Update() From 628b8be15d8aa79fe67944dc20eced20b9f864c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 15:36:44 +0900 Subject: [PATCH 17/46] Implement ModWithVisibilityAdjustment --- .../Mods/CatchModHidden.cs | 6 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 14 ++- osu.Game/Rulesets/Mods/ModHidden.cs | 50 ++++---- .../Mods/ModWithVisibilityAdjustment.cs | 114 ++++++++++++++++++ 4 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index ee88edbea1..4b008d2734 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -17,9 +17,11 @@ namespace osu.Game.Rulesets.Catch.Mods private const double fade_out_offset_multiplier = 0.6; private const double fade_out_duration_multiplier = 0.44; - protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - if (!(drawable is DrawableCatchHitObject catchDrawable)) + base.ApplyNormalVisibilityState(hitObject, state); + + if (!(hitObject is DrawableCatchHitObject catchDrawable)) return; if (catchDrawable.NestedHitObjects.Any()) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index f69cacd432..db5fbb0dd6 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; - protected override bool IsFirstHideableObject(DrawableHitObject hitObject) => !(hitObject is DrawableSpinner); + protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner); public override void ApplyToDrawableHitObjects(IEnumerable drawables) { @@ -42,9 +42,17 @@ namespace osu.Game.Rulesets.Osu.Mods private double lastSliderHeadFadeOutStartTime; private double lastSliderHeadFadeOutDuration; - protected override void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, true); + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + base.ApplyIncreasedVisibilityState(hitObject, state); + applyState(hitObject, true); + } - protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, false); + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + base.ApplyNormalVisibilityState(hitObject, state); + applyState(hitObject, false); + } private void applyState(DrawableHitObject drawable, bool increaseVisibility) { diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index ad01bf036c..f35546d400 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -1,19 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Configuration; +using System; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects, IApplicableToScoreProcessor + public abstract class ModHidden : ModWithVisibilityAdjustment, IApplicableToScoreProcessor { public override string Name => "Hidden"; public override string Acronym => "HD"; @@ -21,37 +18,14 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override bool Ranked => true; - protected Bindable IncreaseFirstObjectVisibility = new Bindable(); - /// /// Check whether the provided hitobject should be considered the "first" hideable object. /// Can be used to skip spinners, for instance. /// /// The hitobject to check. + [Obsolete("Use IsFirstAdjustableObject() instead.")] protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true; - public void ReadFromConfig(OsuConfigManager config) - { - IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); - } - - public virtual void ApplyToDrawableHitObjects(IEnumerable drawables) - { - if (IncreaseFirstObjectVisibility.Value) - { - drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h)); - - var firstObject = drawables.FirstOrDefault(); - if (firstObject != null) - firstObject.ApplyCustomUpdateState += ApplyFirstObjectIncreaseVisibilityState; - - drawables = drawables.Skip(1); - } - - foreach (var dho in drawables) - dho.ApplyCustomUpdateState += ApplyHiddenState; - } - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+ @@ -73,11 +47,28 @@ namespace osu.Game.Rulesets.Mods } } + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + base.ApplyIncreasedVisibilityState(hitObject, state); +#pragma warning disable 618 + ApplyFirstObjectIncreaseVisibilityState(hitObject, state); +#pragma warning restore 618 + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + base.ApplyNormalVisibilityState(hitObject, state); +#pragma warning disable 618 + ApplyHiddenState(hitObject, state); +#pragma warning restore 618 + } + /// /// Apply a special visibility state to the first object in a beatmap, if the user chooses to turn on the "increase first object visibility" setting. /// /// The hit object to apply the state change to. /// The state of the hit object. + [Obsolete("Use ApplyIncreasedVisibilityState() instead.")] protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state) { } @@ -87,6 +78,7 @@ namespace osu.Game.Rulesets.Mods /// /// The hit object to apply the state change to. /// The state of the hit object. + [Obsolete("Use ApplyNormalVisibilityState() instead.")] protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { } diff --git a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs new file mode 100644 index 0000000000..89d1837348 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs @@ -0,0 +1,114 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModWithVisibilityAdjustment : Mod, IReadFromConfig, IApplicableToBeatmap, IApplicableToDrawableHitObjects + { + /// + /// The first adjustable object. + /// + protected HitObject FirstObject { get; private set; } + + /// + /// Whether the visibility of should be increased. + /// + protected readonly Bindable IncreaseFirstObjectVisibility = new Bindable(); + + /// + /// Check whether the provided hitobject should be considered the "first" adjustable object. + /// Can be used to skip spinners, for instance. + /// + /// The hitobject to check. + protected virtual bool IsFirstAdjustableObject(HitObject hitObject) => true; + + /// + /// Apply a special increased-visibility state to the first adjustable object.. + /// Only applicable if the user chooses to turn on the "increase first object visibility" setting. + /// + /// The hit object to apply the state change to. + /// The state of the hit object. + protected virtual void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + /// + /// Apply a normal visibility state adjustment to an object. + /// + /// The hit object to apply the state change to. + /// The state of the hit object. + protected virtual void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + public virtual void ReadFromConfig(OsuConfigManager config) + { + config.BindWith(OsuSetting.IncreaseFirstObjectVisibility, IncreaseFirstObjectVisibility); + } + + public virtual void ApplyToBeatmap(IBeatmap beatmap) + { + FirstObject = getFirstAdjustableObjectRecursive(beatmap.HitObjects); + + HitObject getFirstAdjustableObjectRecursive(IReadOnlyList hitObjects) + { + foreach (var h in hitObjects) + { + if (IsFirstAdjustableObject(h)) + return h; + + var nestedResult = getFirstAdjustableObjectRecursive(h.NestedHitObjects); + if (nestedResult != null) + return nestedResult; + } + + return null; + } + } + + public virtual void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var dho in drawables) + { + dho.ApplyCustomUpdateState += (o, state) => + { + // Increased visibility is applied to the entire first object, including all of its nested hitobjects. + if (IncreaseFirstObjectVisibility.Value && isObjectEqualToOrNestedIn(o.HitObject, FirstObject)) + ApplyIncreasedVisibilityState(o, state); + else + ApplyNormalVisibilityState(o, state); + }; + } + } + + /// + /// Checks whether a given object is nested within a target. + /// + /// The to check. + /// The which may be equal to or contain as a nested object. + /// Whether is equal to or nested within . + private bool isObjectEqualToOrNestedIn(HitObject toCheck, HitObject target) + { + if (target == null) + return false; + + if (toCheck == target) + return true; + + foreach (var h in target.NestedHitObjects) + { + if (isObjectEqualToOrNestedIn(toCheck, h)) + return true; + } + + return false; + } + } +} From 54f927ee11e7a114862ad2625f7ca9bc10bfd794 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 15:41:37 +0900 Subject: [PATCH 18/46] Move casts to DrawableHitObject instead --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 19502369c4..321eeeab65 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -4,12 +4,15 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.Osu.Scoring; @@ -17,10 +20,6 @@ using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Skinning; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Game.Rulesets.Osu.Configuration; -using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.UI @@ -96,6 +95,8 @@ namespace osu.Game.Rulesets.Osu.UI public override void Add(DrawableHitObject h) { + DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; + h.OnNewResult += onNewResult; h.OnLoadComplete += d => { @@ -108,18 +109,19 @@ namespace osu.Game.Rulesets.Osu.UI base.Add(h); - DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; osuHitObject.CheckHittable = hitPolicy.IsHittable; - followPoints.AddFollowPoints((OsuHitObject)h.HitObject); + followPoints.AddFollowPoints(osuHitObject.HitObject); } public override bool Remove(DrawableHitObject h) { + DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; + bool result = base.Remove(h); if (result) - followPoints.RemoveFollowPoints((OsuHitObject)h.HitObject); + followPoints.RemoveFollowPoints(osuHitObject.HitObject); return result; } From 77a618dd71044d185d69d3e178a5bffcbc0f38ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 15:52:06 +0900 Subject: [PATCH 19/46] Use class with other osu! mods --- .../Mods/OsuModObjectScaleTween.cs | 31 ++++-------------- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 32 ++++--------------- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 20 +++--------- 3 files changed, 19 insertions(+), 64 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index 06ba4cde4a..cd2e2e092a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -17,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Adjusts the size of hit objects during their fade in animation. /// - public abstract class OsuModObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + public abstract class OsuModObjectScaleTween : ModWithVisibilityAdjustment { public override ModType Type => ModType.Fun; @@ -27,33 +24,19 @@ namespace osu.Game.Rulesets.Osu.Mods protected virtual float EndScale => 1; - private Bindable increaseFirstObjectVisibility = new Bindable(); - public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn), typeof(OsuModTraceable) }; - public void ReadFromConfig(OsuConfigManager config) + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + base.ApplyNormalVisibilityState(hitObject, state); + applyCustomState(hitObject, state); } - public void ApplyToDrawableHitObjects(IEnumerable drawables) + private void applyCustomState(DrawableHitObject drawable, ArmedState state) { - foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) - { - switch (drawable) - { - case DrawableSpinner _: - continue; + if (drawable is DrawableSpinner) + return; - default: - drawable.ApplyCustomUpdateState += ApplyCustomState; - break; - } - } - } - - protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) - { var h = (OsuHitObject)drawable.HitObject; // apply grow effect diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 940c888f3a..34c94fa7e0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -2,12 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; @@ -16,7 +12,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects, IReadFromConfig + public class OsuModSpinIn : ModWithVisibilityAdjustment { public override string Name => "Spin In"; public override string Acronym => "SI"; @@ -31,31 +27,17 @@ namespace osu.Game.Rulesets.Osu.Mods private const int rotate_offset = 360; private const float rotate_starting_width = 2; - private Bindable increaseFirstObjectVisibility = new Bindable(); - - public void ReadFromConfig(OsuConfigManager config) + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); - } - - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) - { - switch (drawable) - { - case DrawableSpinner _: - continue; - - default: - drawable.ApplyCustomUpdateState += applyZoomState; - break; - } - } + base.ApplyNormalVisibilityState(hitObject, state); + applyZoomState(hitObject, state); } private void applyZoomState(DrawableHitObject drawable, ArmedState state) { + if (drawable is DrawableSpinner) + return; + var h = (OsuHitObject)drawable.HitObject; switch (drawable) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index bb2213aa31..9349dc6a78 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -2,12 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using osu.Framework.Bindables; -using System.Collections.Generic; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -15,7 +11,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModTraceable : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + internal class OsuModTraceable : ModWithVisibilityAdjustment { public override string Name => "Traceable"; public override string Acronym => "TC"; @@ -24,20 +20,14 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; - private Bindable increaseFirstObjectVisibility = new Bindable(); - public void ReadFromConfig(OsuConfigManager config) + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + base.ApplyNormalVisibilityState(hitObject, state); + applyTraceableState(hitObject, state); } - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0)) - drawable.ApplyCustomUpdateState += ApplyTraceableState; - } - - protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state) + private void applyTraceableState(DrawableHitObject drawable, ArmedState state) { if (!(drawable is DrawableOsuHitObject)) return; From a219aa7ba272d31b32a6f5f2c86511f365a8da6e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 15:53:35 +0900 Subject: [PATCH 20/46] Add xmldoc --- osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs index 89d1837348..fd563f4261 100644 --- a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs +++ b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs @@ -10,6 +10,10 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mods { + /// + /// A which applies visibility adjustments to s + /// with an optional increased visibility adjustment depending on the user's "increase first object visibility" setting. + /// public abstract class ModWithVisibilityAdjustment : Mod, IReadFromConfig, IApplicableToBeatmap, IApplicableToDrawableHitObjects { /// From cc518feca7718e8ebb1c89bb6d14f08a0d806a39 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 16:03:10 +0900 Subject: [PATCH 21/46] Make methods abstract --- osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs | 6 +++--- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 6 +++--- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 6 +++--- osu.Game/Rulesets/Mods/ModHidden.cs | 2 -- osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs | 8 ++------ 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index cd2e2e092a..d1be162f73 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn), typeof(OsuModTraceable) }; - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); - applyCustomState(hitObject, state); } + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyCustomState(hitObject, state); + private void applyCustomState(DrawableHitObject drawable, ArmedState state) { if (drawable is DrawableSpinner) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index 34c94fa7e0..96ba58da23 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -27,12 +27,12 @@ namespace osu.Game.Rulesets.Osu.Mods private const int rotate_offset = 360; private const float rotate_starting_width = 2; - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); - applyZoomState(hitObject, state); } + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyZoomState(hitObject, state); + private void applyZoomState(DrawableHitObject drawable, ArmedState state) { if (drawable is DrawableSpinner) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 9349dc6a78..b7e60295cb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); - applyTraceableState(hitObject, state); } + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTraceableState(hitObject, state); + private void applyTraceableState(DrawableHitObject drawable, ArmedState state) { if (!(drawable is DrawableOsuHitObject)) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index f35546d400..b88d785ff1 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -49,7 +49,6 @@ namespace osu.Game.Rulesets.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyIncreasedVisibilityState(hitObject, state); #pragma warning disable 618 ApplyFirstObjectIncreaseVisibilityState(hitObject, state); #pragma warning restore 618 @@ -57,7 +56,6 @@ namespace osu.Game.Rulesets.Mods protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); #pragma warning disable 618 ApplyHiddenState(hitObject, state); #pragma warning restore 618 diff --git a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs index fd563f4261..5bbd02cf6c 100644 --- a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs +++ b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs @@ -39,18 +39,14 @@ namespace osu.Game.Rulesets.Mods /// /// The hit object to apply the state change to. /// The state of the hit object. - protected virtual void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } + protected abstract void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state); /// /// Apply a normal visibility state adjustment to an object. /// /// The hit object to apply the state change to. /// The state of the hit object. - protected virtual void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } + protected abstract void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state); public virtual void ReadFromConfig(OsuConfigManager config) { From f513c95ab215de244a428a233c2063e00f3d9545 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 16:04:42 +0900 Subject: [PATCH 22/46] Use class with transform and wiggle mods --- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 11 ++++------- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 5e80d08667..b5905d7015 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mods; @@ -13,7 +12,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects + internal class OsuModTransform : ModWithVisibilityAdjustment { public override string Name => "Transform"; public override string Acronym => "TR"; @@ -25,11 +24,9 @@ namespace osu.Game.Rulesets.Osu.Mods private float theta; - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables) - drawable.ApplyCustomUpdateState += applyTransform; - } + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state); + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state); private void applyTransform(DrawableHitObject drawable, ArmedState state) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index 3cad52faeb..9c5e41f245 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mods; @@ -13,7 +12,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects + internal class OsuModWiggle : ModWithVisibilityAdjustment { public override string Name => "Wiggle"; public override string Acronym => "WG"; @@ -26,11 +25,9 @@ namespace osu.Game.Rulesets.Osu.Mods private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles private const int wiggle_strength = 10; // Higher = stronger wiggles - public void ApplyToDrawableHitObjects(IEnumerable drawables) - { - foreach (var drawable in drawables) - drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; - } + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => drawableOnApplyCustomUpdateState(hitObject, state); + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => drawableOnApplyCustomUpdateState(hitObject, state); private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) { From d1fa391d251e0d67a02b2c8eb68e1b777567c257 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 16:12:55 +0900 Subject: [PATCH 23/46] Make OsuModHidden apply fadein adjustment on custom state update --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index db5fbb0dd6..025e202666 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -27,18 +26,20 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToDrawableHitObjects(IEnumerable drawables) { - static void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier; - - foreach (var d in drawables.OfType()) - { - adjustFadeIn(d.HitObject); - foreach (var h in d.HitObject.NestedHitObjects.OfType()) - adjustFadeIn(h); - } + foreach (var d in drawables) + d.ApplyCustomUpdateState += applyFadeInAdjustment; base.ApplyToDrawableHitObjects(drawables); } + private void applyFadeInAdjustment(DrawableHitObject hitObject, ArmedState state) + { + if (!(hitObject is DrawableOsuHitObject d)) + return; + + d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier; + } + private double lastSliderHeadFadeOutStartTime; private double lastSliderHeadFadeOutDuration; From 414daab1dc685ec46ffc7f196d236507a66362bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 16:14:22 +0900 Subject: [PATCH 24/46] Fix paused samples potentially getting stuck in a playing state in rapid toggling Closes #10693. Should be obvious why. --- osu.Game/Skinning/PausableSkinnableSound.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index d340f67575..4f09aec0b6 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Screens.Play; @@ -25,6 +26,8 @@ namespace osu.Game.Skinning private readonly IBindable samplePlaybackDisabled = new Bindable(); + private ScheduledDelegate scheduledStart; + [BackgroundDependencyLoader(true)] private void load(ISamplePlaybackDisabler samplePlaybackDisabler) { @@ -39,12 +42,14 @@ namespace osu.Game.Skinning // let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off). if (!Looping) return; + cancelPendingStart(); + if (disabled.NewValue) base.Stop(); else { // schedule so we don't start playing a sample which is no longer alive. - Schedule(() => + scheduledStart = Schedule(() => { if (RequestedPlaying) base.Play(); @@ -56,6 +61,7 @@ namespace osu.Game.Skinning public override void Play() { + cancelPendingStart(); RequestedPlaying = true; if (samplePlaybackDisabled.Value) @@ -66,8 +72,15 @@ namespace osu.Game.Skinning public override void Stop() { + cancelPendingStart(); RequestedPlaying = false; base.Stop(); } + + private void cancelPendingStart() + { + scheduledStart?.Cancel(); + scheduledStart = null; + } } } From 2d0c62dce274e1d53c3e3adf11cf560844aa8f89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 16:37:47 +0900 Subject: [PATCH 25/46] Fix SkinnableTestScene's autosize propagation Regressed with recent DrawableHitObject changes (moving of RelativeSizeAxes specifications out of constructors). --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 41 +++++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index fe4f735325..07e45f25cf 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -65,17 +65,15 @@ namespace osu.Game.Tests.Visual private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap) { var created = creationFunction(); + createdDrawables.Add(created); - var autoSize = created.RelativeSizeAxes == Axes.None; + SkinProvidingContainer mainProvider; + Container childContainer; + OutlineBox outlineBox; + SkinProvidingContainer skinProvider; - var mainProvider = new SkinProvidingContainer(skin) - { - RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, - AutoSizeAxes = autoSize ? Axes.Both : Axes.None, - }; - - return new Container + var children = new Container { RelativeSizeAxes = Axes.Both, BorderColour = Color4.White, @@ -96,27 +94,38 @@ namespace osu.Game.Tests.Visual Scale = new Vector2(1.5f), Padding = new MarginPadding(5), }, - new Container + childContainer = new Container { - RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, - AutoSizeAxes = autoSize ? Axes.Both : Axes.None, Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] { - new OutlineBox { Alpha = autoSize ? 1 : 0 }, - mainProvider.WithChild( - new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) + outlineBox = new OutlineBox(), + (mainProvider = new SkinProvidingContainer(skin)).WithChild( + skinProvider = new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap)) { Child = created, - RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None, - AutoSizeAxes = autoSize ? Axes.Both : Axes.None, } ) } }, } }; + + Schedule(() => + { + var autoSize = created.RelativeSizeAxes == Axes.None; + + foreach (var c in new[] { mainProvider, childContainer, skinProvider }) + { + c.RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None; + c.AutoSizeAxes = autoSize ? Axes.Both : Axes.None; + } + + outlineBox.Alpha = autoSize ? 1 : 0; + }); + + return children; } /// From 9d5b1ec28c0281bed94908f6e2557e8d9169f2cf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 5 Nov 2020 17:03:53 +0900 Subject: [PATCH 26/46] Add removal dates --- osu.Game/Rulesets/Mods/ModHidden.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index b88d785ff1..df421adbe5 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods /// Can be used to skip spinners, for instance. /// /// The hitobject to check. - [Obsolete("Use IsFirstAdjustableObject() instead.")] + [Obsolete("Use IsFirstAdjustableObject() instead.")] // Can be removed 20210506 protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mods /// /// The hit object to apply the state change to. /// The state of the hit object. - [Obsolete("Use ApplyIncreasedVisibilityState() instead.")] + [Obsolete("Use ApplyIncreasedVisibilityState() instead.")] // Can be removed 20210506 protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state) { } @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Mods /// /// The hit object to apply the state change to. /// The state of the hit object. - [Obsolete("Use ApplyNormalVisibilityState() instead.")] + [Obsolete("Use ApplyNormalVisibilityState() instead.")] // Can be removed 20210506 protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { } From 9c91f16e3a2425f248a5d040525322737fd639ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 17:24:59 +0900 Subject: [PATCH 27/46] Update sizing as early as possible in addition to scheduling (to handle any dependent edge cases) --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index 07e45f25cf..d5fcf8bae3 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -112,7 +112,15 @@ namespace osu.Game.Tests.Visual } }; - Schedule(() => + // run this once initially to bring things into a sane state as early as possible. + updateSizing(); + + // run this once after construction to handle the case the changes are made in a BDL/LoadComplete call. + Schedule(updateSizing); + + return children; + + void updateSizing() { var autoSize = created.RelativeSizeAxes == Axes.None; @@ -123,9 +131,7 @@ namespace osu.Game.Tests.Visual } outlineBox.Alpha = autoSize ? 1 : 0; - }); - - return children; + } } /// From 8aa0a698d986f11a4a054522dee9e81e57937f7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 17:26:41 +0900 Subject: [PATCH 28/46] Avoid errors due to second set --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index d5fcf8bae3..68098f9d3b 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -126,6 +126,9 @@ namespace osu.Game.Tests.Visual foreach (var c in new[] { mainProvider, childContainer, skinProvider }) { + c.RelativeSizeAxes = Axes.None; + c.AutoSizeAxes = Axes.None; + c.RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None; c.AutoSizeAxes = autoSize ? Axes.Both : Axes.None; } From 7d33dc3deae7df07cfa17afd680825788c2df372 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 17:12:18 +0900 Subject: [PATCH 29/46] Reorder spinner tests to promote hit being the first to display --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 94d1cb8864..496b1b3559 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Osu.Tests private TestDrawableSpinner drawableSpinner; - [TestCase(false)] [TestCase(true)] + [TestCase(false)] public void TestVariousSpinners(bool autoplay) { string term = autoplay ? "Hit" : "Miss"; From 82d8c1bbea7a37fde1382fd56b6cb6f0acc1f41b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 17:12:35 +0900 Subject: [PATCH 30/46] Add support for spinner glow --- .../Skinning/LegacyNewStyleSpinner.cs | 58 ++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index b65e5a784c..31e2ab1239 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning /// public class LegacyNewStyleSpinner : CompositeDrawable { + private Sprite glow; private Sprite discBottom; private Sprite discTop; private Sprite spinningMiddle; @@ -30,6 +31,8 @@ namespace osu.Game.Rulesets.Osu.Skinning private const float final_scale = 0.625f; + private readonly Color4 glowColour = new Color4(3, 151, 255, 255); + [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { @@ -39,6 +42,14 @@ namespace osu.Game.Rulesets.Osu.Skinning InternalChildren = new Drawable[] { + glow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-glow"), + Blending = BlendingParameters.Additive, + Colour = glowColour, + }, discBottom = new Sprite { Anchor = Anchor.Centre, @@ -76,23 +87,38 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - if (!(drawableHitObject is DrawableSpinner d)) - return; - - Spinner spinner = d.HitObject; - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) - this.FadeOut(); - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) - this.FadeInFromZero(spinner.TimeFadeIn / 2); - - using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + switch (drawableHitObject) { - fixedMiddle.FadeColour(Color4.White); + case DrawableSpinner d: + Spinner spinner = d.HitObject; - using (BeginDelayedSequence(spinner.TimePreempt, true)) - fixedMiddle.FadeColour(Color4.Red, spinner.Duration); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + this.FadeOut(); + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) + this.FadeInFromZero(spinner.TimeFadeIn / 2); + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true)) + { + fixedMiddle.FadeColour(Color4.White); + + using (BeginDelayedSequence(spinner.TimePreempt, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.Duration); + } + + if (state == ArmedState.Hit) + { + using (BeginAbsoluteSequence(d.HitStateUpdateTime)) + glow.FadeOut(300); + } + + break; + + case DrawableSpinnerBonusTick _: + if (state == ArmedState.Hit) + glow.FlashColour(Color4.White, 200); + + break; } } @@ -102,6 +128,8 @@ namespace osu.Game.Rulesets.Osu.Skinning spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; discBottom.Rotation = discTop.Rotation / 3; + glow.Alpha = drawableSpinner.Progress; + Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f)); } From 2d50a7b61644d0635da7550457957b61d6855005 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 17:38:02 +0900 Subject: [PATCH 31/46] Fix a few xmldoc typos --- osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs index 5bbd02cf6c..5b119b5e46 100644 --- a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs +++ b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs @@ -34,18 +34,18 @@ namespace osu.Game.Rulesets.Mods protected virtual bool IsFirstAdjustableObject(HitObject hitObject) => true; /// - /// Apply a special increased-visibility state to the first adjustable object.. + /// Apply a special increased-visibility state to the first adjustable object. /// Only applicable if the user chooses to turn on the "increase first object visibility" setting. /// /// The hit object to apply the state change to. - /// The state of the hit object. + /// The state of the hitobject. protected abstract void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state); /// /// Apply a normal visibility state adjustment to an object. /// /// The hit object to apply the state change to. - /// The state of the hit object. + /// The state of the hitobject. protected abstract void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state); public virtual void ReadFromConfig(OsuConfigManager config) From 64e3325b4140bf89e30cb8cb2e7f9fa90dd64716 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 18:00:26 +0900 Subject: [PATCH 32/46] Abstract out common part of legacy spinner implementations Some elements going forward will be shared, so it makes sense to have a common base class to add these shared elements. --- .../Skinning/LegacyNewStyleSpinner.cs | 33 +++----------- .../Skinning/LegacyOldStyleSpinner.cs | 31 +++---------- .../Skinning/LegacySpinner.cs | 44 +++++++++++++++++++ 3 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 31e2ab1239..5b6aac8f08 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; @@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning /// Legacy skinned spinner with two main spinning layers, one fixed overlay and one final spinning overlay. /// No background layer. /// - public class LegacyNewStyleSpinner : CompositeDrawable + public class LegacyNewStyleSpinner : LegacySpinner { private Sprite glow; private Sprite discBottom; @@ -27,17 +26,13 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite spinningMiddle; private Sprite fixedMiddle; - private DrawableSpinner drawableSpinner; - private const float final_scale = 0.625f; private readonly Color4 glowColour = new Color4(3, 151, 255, 255); [BackgroundDependencyLoader] - private void load(ISkinSource source, DrawableHitObject drawableObject) + private void load(ISkinSource source) { - drawableSpinner = (DrawableSpinner)drawableObject; - Scale = new Vector2(final_scale); InternalChildren = new Drawable[] @@ -77,16 +72,10 @@ namespace osu.Game.Rulesets.Osu.Skinning }; } - protected override void LoadComplete() + protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - base.LoadComplete(); + base.UpdateStateTransforms(drawableHitObject, state); - drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); - } - - private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) - { switch (drawableHitObject) { case DrawableSpinner d: @@ -125,20 +114,12 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void Update() { base.Update(); - spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; + spinningMiddle.Rotation = discTop.Rotation = DrawableSpinner.RotationTracker.Rotation; discBottom.Rotation = discTop.Rotation / 3; - glow.Alpha = drawableSpinner.Progress; + glow.Alpha = DrawableSpinner.Progress; - Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f)); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (drawableSpinner != null) - drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms; + Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f)); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 1954ff6e38..56702e6712 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -18,9 +18,8 @@ namespace osu.Game.Rulesets.Osu.Skinning /// /// Legacy skinned spinner with one main spinning layer and a background layer. /// - public class LegacyOldStyleSpinner : CompositeDrawable + public class LegacyOldStyleSpinner : LegacySpinner { - private DrawableSpinner drawableSpinner; private Sprite disc; private Sprite metreSprite; private Container metre; @@ -31,14 +30,10 @@ namespace osu.Game.Rulesets.Osu.Skinning private const float final_metre_height = 692 * sprite_scale; [BackgroundDependencyLoader] - private void load(ISkinSource source, DrawableHitObject drawableObject) + private void load(ISkinSource source) { spinnerBlink = source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true; - drawableSpinner = (DrawableSpinner)drawableObject; - - RelativeSizeAxes = Axes.Both; - InternalChild = new Container { // the old-style spinner relied heavily on absolute screen-space coordinate values. @@ -85,16 +80,10 @@ namespace osu.Game.Rulesets.Osu.Skinning }; } - protected override void LoadComplete() + protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { - base.LoadComplete(); + base.UpdateStateTransforms(drawableHitObject, state); - drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; - updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); - } - - private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) - { if (!(drawableHitObject is DrawableSpinner d)) return; @@ -110,11 +99,11 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void Update() { base.Update(); - disc.Rotation = drawableSpinner.RotationTracker.Rotation; + disc.Rotation = DrawableSpinner.RotationTracker.Rotation; // careful: need to call this exactly once for all calculations in a frame // as the function has a random factor in it - var metreHeight = getMetreHeight(drawableSpinner.Progress); + var metreHeight = getMetreHeight(DrawableSpinner.Progress); // hack to make the metre blink up from below than down from above. // move down the container to be able to apply masking for the metre, @@ -140,13 +129,5 @@ namespace osu.Game.Rulesets.Osu.Skinning return (float)barCount / total_bars * final_metre_height; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (drawableSpinner != null) - drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms; - } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs new file mode 100644 index 0000000000..efbafdc17a --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public abstract class LegacySpinner : CompositeDrawable + { + protected DrawableSpinner DrawableSpinner { get; private set; } + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableHitObject) + { + RelativeSizeAxes = Axes.Both; + + DrawableSpinner = (DrawableSpinner)drawableHitObject; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms; + UpdateStateTransforms(DrawableSpinner, DrawableSpinner.State.Value); + } + + protected virtual void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) + { + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (DrawableSpinner != null) + DrawableSpinner.ApplyCustomUpdateState -= UpdateStateTransforms; + } + } +} From 11c18952e3138c7bd2f85a8a9336e55c888f6c38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 18:03:24 +0900 Subject: [PATCH 33/46] Allow children to be added in base class --- osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs | 4 ++-- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 5b6aac8f08..f77a722376 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { Scale = new Vector2(final_scale); - InternalChildren = new Drawable[] + AddRangeInternal(new Drawable[] { glow = new Sprite { @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Origin = Anchor.Centre, Texture = source.GetTexture("spinner-middle2") } - }; + }); } protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 56702e6712..dd63879247 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { spinnerBlink = source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true; - InternalChild = new Container + AddInternal(new Container { // the old-style spinner relied heavily on absolute screen-space coordinate values. // wrap everything in a container simulating absolute coords to preserve alignment @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Skinning } } } - }; + }); } protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) From 5ec6011340cb05ece29f815e60afc6577c05bc2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 18:12:02 +0900 Subject: [PATCH 34/46] Apply new style spinner scale to only local sprites --- .../Skinning/LegacyNewStyleSpinner.cs | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index f77a722376..3a60f54d24 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; @@ -30,44 +31,51 @@ namespace osu.Game.Rulesets.Osu.Skinning private readonly Color4 glowColour = new Color4(3, 151, 255, 255); + private Container scaleContainer; + [BackgroundDependencyLoader] private void load(ISkinSource source) { - Scale = new Vector2(final_scale); - - AddRangeInternal(new Drawable[] + AddInternal(scaleContainer = new Container { - glow = new Sprite + Scale = new Vector2(final_scale), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-glow"), - Blending = BlendingParameters.Additive, - Colour = glowColour, - }, - discBottom = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-bottom") - }, - discTop = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-top") - }, - fixedMiddle = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-middle") - }, - spinningMiddle = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-middle2") + glow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-glow"), + Blending = BlendingParameters.Additive, + Colour = glowColour, + }, + discBottom = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-bottom") + }, + discTop = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-top") + }, + fixedMiddle = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-middle") + }, + spinningMiddle = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-middle2") + } } }); } @@ -119,7 +127,7 @@ namespace osu.Game.Rulesets.Osu.Skinning glow.Alpha = DrawableSpinner.Progress; - Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f)); + scaleContainer.Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f)); } } } From 3ec813da03c636d3fb936c1da04be597a45f95d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 18:12:13 +0900 Subject: [PATCH 35/46] Add "spin" sprite --- .../Skinning/LegacySpinner.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs index efbafdc17a..62df4989ba 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Skinning { @@ -13,12 +16,26 @@ namespace osu.Game.Rulesets.Osu.Skinning { protected DrawableSpinner DrawableSpinner { get; private set; } + private Sprite spin; + [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableHitObject) + private void load(DrawableHitObject drawableHitObject, ISkinSource source) { RelativeSizeAxes = Axes.Both; DrawableSpinner = (DrawableSpinner)drawableHitObject; + + AddRangeInternal(new[] + { + spin = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Depth = float.MinValue, + Texture = source.GetTexture("spinner-spin"), + Y = 120 // todo: make match roughly? + }, + }); } protected override void LoadComplete() @@ -31,6 +48,25 @@ namespace osu.Game.Rulesets.Osu.Skinning protected virtual void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) { + switch (drawableHitObject) + { + case DrawableSpinner d: + double fadeOutLength = Math.Min(400, d.HitObject.Duration); + + using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - fadeOutLength, true)) + spin.FadeOutFromOne(fadeOutLength); + + break; + + case DrawableSpinnerTick d: + if (state == ArmedState.Hit) + { + using (BeginAbsoluteSequence(d.HitStateUpdateTime, true)) + spin.FadeOut(300); + } + + break; + } } protected override void Dispose(bool isDisposing) From a0b3379909aaaf14fa1bdc5dd31e9d8d380dd5e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 18:58:37 +0900 Subject: [PATCH 36/46] Fix judgement offsets being zero when windows are empty --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index c7e2e0638c..5c3f57c2d0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -546,7 +546,11 @@ namespace osu.Game.Rulesets.Objects.Drawables // Ensure that the judgement is given a valid time offset, because this may not get set by the caller var endTime = HitObject.GetEndTime(); - Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime); + Result.TimeOffset = Time.Current - endTime; + + double missWindow = HitObject.HitWindows.WindowFor(HitResult.Miss); + if (missWindow > 0) + Result.TimeOffset = Math.Min(Result.TimeOffset, missWindow); if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); From 901102918ee29241406f716e3a2c014a8995c116 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 19:05:59 +0900 Subject: [PATCH 37/46] Centralise scale and apply to SPIN text --- osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs | 6 ++---- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 9 ++++----- osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs | 4 ++++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 3a60f54d24..05f4c8e307 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite spinningMiddle; private Sprite fixedMiddle; - private const float final_scale = 0.625f; - private readonly Color4 glowColour = new Color4(3, 151, 255, 255); private Container scaleContainer; @@ -38,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { AddInternal(scaleContainer = new Container { - Scale = new Vector2(final_scale), + Scale = new Vector2(SPRITE_SCALE), Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, @@ -127,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Skinning glow.Alpha = DrawableSpinner.Progress; - scaleContainer.Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f)); + scaleContainer.Scale = new Vector2(SPRITE_SCALE * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f)); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index dd63879247..fba802f085 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -26,8 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning private bool spinnerBlink; - private const float sprite_scale = 1 / 1.6f; - private const float final_metre_height = 692 * sprite_scale; + private const float final_metre_height = 692 * SPRITE_SCALE; [BackgroundDependencyLoader] private void load(ISkinSource source) @@ -50,14 +49,14 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, Texture = source.GetTexture("spinner-background"), - Scale = new Vector2(sprite_scale) + Scale = new Vector2(SPRITE_SCALE) }, disc = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, Texture = source.GetTexture("spinner-circle"), - Scale = new Vector2(sprite_scale) + Scale = new Vector2(SPRITE_SCALE) }, metre = new Container { @@ -73,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Texture = source.GetTexture("spinner-metre"), Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, - Scale = new Vector2(0.625f) + Scale = new Vector2(SPRITE_SCALE) } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs index 62df4989ba..2d0d350fce 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs @@ -9,11 +9,14 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Skinning { public abstract class LegacySpinner : CompositeDrawable { + protected const float SPRITE_SCALE = 0.625f; + protected DrawableSpinner DrawableSpinner { get; private set; } private Sprite spin; @@ -33,6 +36,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Origin = Anchor.Centre, Depth = float.MinValue, Texture = source.GetTexture("spinner-spin"), + Scale = new Vector2(SPRITE_SCALE), Y = 120 // todo: make match roughly? }, }); From 5e387e92cd9e2848c562c0dbc05b72420567de94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 19:16:47 +0900 Subject: [PATCH 38/46] Add positional offset to avoid the SPM counter --- osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs index 2d0d350fce..31350791e3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Depth = float.MinValue, Texture = source.GetTexture("spinner-spin"), Scale = new Vector2(SPRITE_SCALE), - Y = 120 // todo: make match roughly? + Y = 120 - 45 // offset temporarily to avoid overlapping default spin counter }, }); } From d774afd224de8bd014db5ab892f38dd22eb576e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Nov 2020 22:29:44 +0900 Subject: [PATCH 39/46] 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 97812402a3..f56baf4e5f 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 e57807e989..3783ae7d5c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 40ecfffcca..ed3ec9e48b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 1e1569eb5302813472df97ea2e8af0ad11eda1cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 12:40:54 +0900 Subject: [PATCH 40/46] Use int instead of long for user_id fields for now --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 6 +++--- osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs | 2 +- osu.Game/Online/Chat/Channel.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Users/User.cs | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index df4b85b37a..72c6fd8d44 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -250,7 +250,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void EndPlay(int beatmapId) { - ((ISpectatorClient)this).UserFinishedPlaying((int)StreamingUser.Id, new SpectatorState + ((ISpectatorClient)this).UserFinishedPlaying(StreamingUser.Id, new SpectatorState { BeatmapID = beatmapId, RulesetID = 0, @@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.Gameplay } var bundle = new FrameDataBundle(frames); - ((ISpectatorClient)this).UserSentFrames((int)StreamingUser.Id, bundle); + ((ISpectatorClient)this).UserSentFrames(StreamingUser.Id, bundle); if (!sentState) sendState(beatmapId); @@ -293,7 +293,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void sendState(int beatmapId) { sentState = true; - ((ISpectatorClient)this).UserBeganPlaying((int)StreamingUser.Id, new SpectatorState + ((ISpectatorClient)this).UserBeganPlaying(StreamingUser.Id, new SpectatorState { BeatmapID = beatmapId, RulesetID = 0, diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index 7196f47bd6..582f72429b 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -277,7 +277,7 @@ namespace osu.Game.Tournament.Screens.Editors userId.Value = user.Id.ToString(); userId.BindValueChanged(idString => { - long.TryParse(idString.NewValue, out var parsed); + int.TryParse(idString.NewValue, out var parsed); user.Id = parsed; diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 1ca14256e5..6d0160fbc4 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -61,7 +61,7 @@ namespace osu.Game.Online.API.Requests.Responses private int[] ratings { get; set; } [JsonProperty(@"user_id")] - private long creatorId + private int creatorId { set => Author.Id = value; } diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs index 5891391e83..024e1ce048 100644 --- a/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.API.Requests.Responses public string OsuUsername { get; set; } [JsonProperty("user_id")] - public long? UserId { get; set; } + public int? UserId { get; set; } [JsonProperty("user_url")] public string UserUrl { get; set; } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 8c1e1ad128..187a3e5dfc 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.Chat public readonly ObservableCollection Users = new ObservableCollection(); [JsonProperty(@"users")] - private long[] userIds + private int[] userIds { set { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a0ddab702e..64f8d4415b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -278,7 +278,7 @@ namespace osu.Game break; case LinkAction.OpenUserProfile: - if (long.TryParse(link.Argument, out long userId)) + if (int.TryParse(link.Argument, out int userId)) ShowUser(userId); break; @@ -321,7 +321,7 @@ namespace osu.Game /// Show a user's profile as an overlay. /// /// The user to display. - public void ShowUser(long userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); + public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId)); /// /// Show a beatmap's set as an overlay, displaying the given beatmap. diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index d52ad84592..81027667fa 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays { } - public void ShowUser(long userId) => ShowUser(new User { Id = userId }); + public void ShowUser(int userId) => ShowUser(new User { Id = userId }); public void ShowUser(User user, bool fetchOnline = true) { diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 596e98a6bd..f5192f3a40 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -123,7 +123,7 @@ namespace osu.Game.Scoring [JsonIgnore] [Column("UserID")] - public long? UserID + public int? UserID { get => User?.Id ?? 1; set diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 89786e3bd8..1fbc3d06f4 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -12,7 +12,7 @@ namespace osu.Game.Users public class User : IEquatable { [JsonProperty(@"id")] - public long Id = 1; + public int Id = 1; [JsonProperty(@"join_date")] public DateTimeOffset JoinDate; From 5113d4af8ff12c695e1e6838e387d76763ddee75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:14:23 +0900 Subject: [PATCH 41/46] Rename BeatmapDifficultyManager to BeatmapDifficultyCache --- .../Beatmaps/BeatmapDifficultyManagerTest.cs | 10 +++++----- ...ifficultyManager.cs => BeatmapDifficultyCache.cs} | 12 ++++++++---- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 6 +++--- osu.Game/OsuGameBase.cs | 8 ++++---- osu.Game/Scoring/ScoreManager.cs | 10 +++++----- osu.Game/Scoring/ScorePerformanceManager.cs | 4 ++-- .../Ranking/Expanded/ExpandedPanelMiddleContent.cs | 4 ++-- .../Screens/Ranking/Expanded/StarRatingDisplay.cs | 2 +- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 4 ++-- .../Select/Carousel/DrawableCarouselBeatmap.cs | 4 ++-- osu.Game/Screens/Select/Details/AdvancedStats.cs | 6 +++--- 12 files changed, 38 insertions(+), 34 deletions(-) rename osu.Game/Beatmaps/{BeatmapDifficultyManager.cs => BeatmapDifficultyCache.cs} (97%) diff --git a/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs b/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs index 7c1ddd757f..ec77f48063 100644 --- a/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs +++ b/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs @@ -14,8 +14,8 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestKeyEqualsWithDifferentModInstances() { - var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); - var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); Assert.That(key1, Is.EqualTo(key2)); } @@ -23,8 +23,8 @@ namespace osu.Game.Tests.Beatmaps [Test] public void TestKeyEqualsWithDifferentModOrder() { - var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); - var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHidden(), new OsuModHardRock() }); + var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() }); + var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHidden(), new OsuModHardRock() }); Assert.That(key1, Is.EqualTo(key2)); } @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps [TestCase(8.3, DifficultyRating.ExpertPlus)] public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket) { - var actualBracket = BeatmapDifficultyManager.GetDifficultyRating(starRating); + var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating); Assert.AreEqual(expectedBracket, actualBracket); } diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs similarity index 97% rename from osu.Game/Beatmaps/BeatmapDifficultyManager.cs rename to osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 9e83738e70..dafe7c19e6 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Threading; @@ -23,10 +23,14 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Beatmaps { - public class BeatmapDifficultyManager : CompositeDrawable + /// + /// A component which performs and acts as a central cache for difficulty calculations of beatmap/ruleset/mod combinations. + /// Currently not persisted between game sessions. + /// + public class BeatmapDifficultyCache : Component { // Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes. - private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyManager)); + private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyCache)); // A permanent cache to prevent re-computations. private readonly ConcurrentDictionary difficultyCache = new ConcurrentDictionary(); @@ -387,6 +391,6 @@ namespace osu.Game.Beatmaps Attributes = null; } - public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(Stars); + public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(Stars); } } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index ffd8d14048..a898e10e4f 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -136,7 +136,7 @@ namespace osu.Game.Beatmaps public List Scores { get; set; } [JsonIgnore] - public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(StarDifficulty); + public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty); public string[] SearchableTerms => new[] { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index a1d5e33d1e..96e18f120a 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -142,7 +142,7 @@ namespace osu.Game.Beatmaps.Drawables private CancellationTokenSource difficultyCancellation; [Resolved] - private BeatmapDifficultyManager difficultyManager { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } public DifficultyRetriever(BeatmapInfo beatmap, RulesetInfo ruleset, IReadOnlyList mods) { @@ -158,8 +158,8 @@ namespace osu.Game.Beatmaps.Drawables { difficultyCancellation = new CancellationTokenSource(); localStarDifficulty = ruleset != null - ? difficultyManager.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token) - : difficultyManager.GetBindableDifficulty(beatmap, difficultyCancellation.Token); + ? difficultyCache.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token) + : difficultyCache.GetBindableDifficulty(beatmap, difficultyCancellation.Token); localStarDifficulty.BindValueChanged(difficulty => StarDifficulty.Value = difficulty.NewValue); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4bc54e7e83..fa183beeb9 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -59,7 +59,7 @@ namespace osu.Game protected ScoreManager ScoreManager; - protected BeatmapDifficultyManager DifficultyManager; + protected BeatmapDifficultyCache DifficultyCache; protected SkinManager SkinManager; @@ -202,7 +202,7 @@ namespace osu.Game dependencies.Cache(FileStore = new FileStore(contextFactory, Storage)); // 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, () => DifficultyManager, LocalConfig)); + 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)); // this should likely be moved to ArchiveModelManager when another case appers where it is necessary @@ -226,8 +226,8 @@ namespace osu.Game ScoreManager.Undelete(getBeatmapScores(item), true); }); - dependencies.Cache(DifficultyManager = new BeatmapDifficultyManager()); - AddInternal(DifficultyManager); + dependencies.Cache(DifficultyCache = new BeatmapDifficultyCache()); + AddInternal(DifficultyCache); var scorePerformanceManager = new ScorePerformanceManager(); dependencies.Cache(scorePerformanceManager); diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index cce6153953..cf1d123c06 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -37,13 +37,13 @@ namespace osu.Game.Scoring private readonly Func beatmaps; [CanBeNull] - private readonly Func difficulties; + private readonly Func difficulties; [CanBeNull] private readonly OsuConfigManager configManager; public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null, - Func difficulties = null, OsuConfigManager configManager = null) + Func difficulties = null, OsuConfigManager configManager = null) : base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost) { this.rulesets = rulesets; @@ -121,14 +121,14 @@ namespace osu.Game.Scoring public readonly Bindable ScoringMode = new Bindable(); private readonly ScoreInfo score; - private readonly Func difficulties; + private readonly Func difficulties; /// /// Creates a new . /// /// The to provide the total score of. - /// A function to retrieve the . - public TotalScoreBindable(ScoreInfo score, Func difficulties) + /// A function to retrieve the . + public TotalScoreBindable(ScoreInfo score, Func difficulties) { this.score = score; this.difficulties = difficulties; diff --git a/osu.Game/Scoring/ScorePerformanceManager.cs b/osu.Game/Scoring/ScorePerformanceManager.cs index ddda1b99af..326a2fce7f 100644 --- a/osu.Game/Scoring/ScorePerformanceManager.cs +++ b/osu.Game/Scoring/ScorePerformanceManager.cs @@ -22,7 +22,7 @@ namespace osu.Game.Scoring private readonly ConcurrentDictionary performanceCache = new ConcurrentDictionary(); [Resolved] - private BeatmapDifficultyManager difficultyManager { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } /// /// Calculates performance for the given . @@ -41,7 +41,7 @@ namespace osu.Game.Scoring private async Task computePerformanceAsync(ScoreInfo score, PerformanceCacheLookup lookupKey, CancellationToken token = default) { - var attributes = await difficultyManager.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, token); + var attributes = await difficultyCache.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, token); // Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value. if (attributes.Attributes == null) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index f9b7625913..33ee5d2ee4 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyManager beatmapDifficultyManager) + private void load(BeatmapDifficultyCache beatmapDifficultyCache) { var beatmap = score.Beatmap; var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Ranking.Expanded Spacing = new Vector2(5, 0), Children = new Drawable[] { - new StarRatingDisplay(beatmapDifficultyManager.GetDifficulty(beatmap, score.Ruleset, score.Mods)) + new StarRatingDisplay(beatmapDifficultyCache.GetDifficulty(beatmap, score.Ruleset, score.Mods)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index ffb12d474b..f7e50fdc8a 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(OsuColour colours, BeatmapDifficultyManager difficultyManager) + private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache) { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2634f117de..04c1f6efe4 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select private readonly IBindable ruleset = new Bindable(); [Resolved] - private BeatmapDifficultyManager difficultyManager { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } private IBindable beatmapDifficulty; @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select cancellationSource = new CancellationTokenSource(); beatmapDifficulty?.UnbindAll(); - beatmapDifficulty = difficultyManager.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); + beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token); beatmapDifficulty.BindValueChanged(_ => updateDisplay()); updateDisplay(); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 49a370724e..e66469ff8d 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Carousel private BeatmapSetOverlay beatmapOverlay { get; set; } [Resolved] - private BeatmapDifficultyManager difficultyManager { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } @@ -216,7 +216,7 @@ namespace osu.Game.Screens.Select.Carousel if (Item.State.Value != CarouselItemState.Collapsed) { // We've potentially cancelled the computation above so a new bindable is required. - starDifficultyBindable = difficultyManager.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token); starDifficultyBindable.BindValueChanged(d => starCounter.Current = (float)d.NewValue.Stars, true); } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 44c328187f..44d908fc46 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Select.Details private IBindable ruleset { get; set; } [Resolved] - private BeatmapDifficultyManager difficultyManager { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; @@ -161,8 +161,8 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource = new CancellationTokenSource(); - normalStarDifficulty = difficultyManager.GetBindableDifficulty(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token); - moddedStarDifficulty = difficultyManager.GetBindableDifficulty(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); + normalStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token); + moddedStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token); normalStarDifficulty.BindValueChanged(_ => updateDisplay()); moddedStarDifficulty.BindValueChanged(_ => updateDisplay(), true); From 14bb079feb84717965b086e0949f865e2aeac486 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:15:33 +0900 Subject: [PATCH 42/46] Rename ScorePerformanceManager to ScorePerformanceCache --- osu.Game/OsuGameBase.cs | 2 +- .../{ScorePerformanceManager.cs => ScorePerformanceCache.cs} | 5 +++-- .../Ranking/Expanded/Statistics/PerformanceStatistic.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) rename osu.Game/Scoring/{ScorePerformanceManager.cs => ScorePerformanceCache.cs} (92%) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fa183beeb9..3da692249d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -229,7 +229,7 @@ namespace osu.Game dependencies.Cache(DifficultyCache = new BeatmapDifficultyCache()); AddInternal(DifficultyCache); - var scorePerformanceManager = new ScorePerformanceManager(); + var scorePerformanceManager = new ScorePerformanceCache(); dependencies.Cache(scorePerformanceManager); AddInternal(scorePerformanceManager); diff --git a/osu.Game/Scoring/ScorePerformanceManager.cs b/osu.Game/Scoring/ScorePerformanceCache.cs similarity index 92% rename from osu.Game/Scoring/ScorePerformanceManager.cs rename to osu.Game/Scoring/ScorePerformanceCache.cs index 326a2fce7f..8b764c75b7 100644 --- a/osu.Game/Scoring/ScorePerformanceManager.cs +++ b/osu.Game/Scoring/ScorePerformanceCache.cs @@ -13,9 +13,10 @@ using osu.Game.Beatmaps; namespace osu.Game.Scoring { /// - /// A global component which calculates and caches results of performance calculations for locally databased scores. + /// A component which performs and acts as a central cache for performance calculations of locally databased scores. + /// Currently not persisted between game sessions. /// - public class ScorePerformanceManager : Component + public class ScorePerformanceCache : Component { // this cache will grow indefinitely per session and should be considered temporary. // this whole component should likely be replaced with database persistence. diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index cd9d8005c6..730221cc4b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics } [BackgroundDependencyLoader] - private void load(ScorePerformanceManager performanceManager) + private void load(ScorePerformanceCache performanceCache) { if (score.PP.HasValue) { @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics } else { - performanceManager.CalculatePerformanceAsync(score, cancellationTokenSource.Token) + performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); } } From 0103b1257574df20dcbe73e19eb0daa2b8ac1a57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:26:18 +0900 Subject: [PATCH 43/46] Add basic base class to begin to standardise function across caching components --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 15 ++++++--------- osu.Game/Database/MemoryCachingComponent.cs | 17 +++++++++++++++++ osu.Game/Scoring/ScorePerformanceCache.cs | 13 ++++--------- .../Expanded/Statistics/PerformanceStatistic.cs | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Database/MemoryCachingComponent.cs diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index dafe7c19e6..3ca0b47121 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -11,11 +11,11 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -27,14 +27,11 @@ namespace osu.Game.Beatmaps /// A component which performs and acts as a central cache for difficulty calculations of beatmap/ruleset/mod combinations. /// Currently not persisted between game sessions. /// - public class BeatmapDifficultyCache : Component + public class BeatmapDifficultyCache : MemoryCachingComponent { // Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes. private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyCache)); - // A permanent cache to prevent re-computations. - private readonly ConcurrentDictionary difficultyCache = new ConcurrentDictionary(); - // All bindables that should be updated along with the current ruleset + mods. private readonly LockedWeakList trackedBindables = new LockedWeakList(); @@ -243,7 +240,7 @@ namespace osu.Game.Beatmaps var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo)); var attributes = calculator.Calculate(key.Mods); - return difficultyCache[key] = new StarDifficulty(attributes); + return Cache[key] = new StarDifficulty(attributes); } catch (BeatmapInvalidForRulesetException e) { @@ -254,7 +251,7 @@ namespace osu.Game.Beatmaps if (rulesetInfo.Equals(beatmapInfo.Ruleset)) { Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset})."); - return difficultyCache[key] = new StarDifficulty(); + return Cache[key] = new StarDifficulty(); } // Check the cache first because this is now a different ruleset than the one previously guarded against. @@ -265,7 +262,7 @@ namespace osu.Game.Beatmaps } catch { - return difficultyCache[key] = new StarDifficulty(); + return Cache[key] = new StarDifficulty(); } } @@ -294,7 +291,7 @@ namespace osu.Game.Beatmaps } key = new DifficultyCacheLookup(beatmapInfo.ID, rulesetInfo.ID.Value, mods); - return difficultyCache.TryGetValue(key, out existingDifficulty); + return Cache.TryGetValue(key, out existingDifficulty); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Database/MemoryCachingComponent.cs b/osu.Game/Database/MemoryCachingComponent.cs new file mode 100644 index 0000000000..85cf3b8af1 --- /dev/null +++ b/osu.Game/Database/MemoryCachingComponent.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 System.Collections.Concurrent; +using osu.Framework.Graphics; + +namespace osu.Game.Database +{ + /// + /// A component which performs lookups (or calculations) and caches the results. + /// Currently not persisted between game sessions. + /// + public abstract class MemoryCachingComponent : Component + { + protected readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + } +} diff --git a/osu.Game/Scoring/ScorePerformanceCache.cs b/osu.Game/Scoring/ScorePerformanceCache.cs index 8b764c75b7..435b93d7af 100644 --- a/osu.Game/Scoring/ScorePerformanceCache.cs +++ b/osu.Game/Scoring/ScorePerformanceCache.cs @@ -2,13 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Database; namespace osu.Game.Scoring { @@ -16,12 +15,8 @@ namespace osu.Game.Scoring /// A component which performs and acts as a central cache for performance calculations of locally databased scores. /// Currently not persisted between game sessions. /// - public class ScorePerformanceCache : Component + public class ScorePerformanceCache : MemoryCachingComponent { - // this cache will grow indefinitely per session and should be considered temporary. - // this whole component should likely be replaced with database persistence. - private readonly ConcurrentDictionary performanceCache = new ConcurrentDictionary(); - [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -34,7 +29,7 @@ namespace osu.Game.Scoring { var lookupKey = new PerformanceCacheLookup(score); - if (performanceCache.TryGetValue(lookupKey, out double performance)) + if (Cache.TryGetValue(lookupKey, out double performance)) return Task.FromResult((double?)performance); return computePerformanceAsync(score, lookupKey, token); @@ -54,7 +49,7 @@ namespace osu.Game.Scoring var total = calculator?.Calculate(); if (total.HasValue) - performanceCache[lookupKey] = total.Value; + Cache[lookupKey] = total.Value; return total; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 730221cc4b..68da4ec724 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics else { performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); } } From 07166ec819970757a77a6646e3d6198162c9171f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:29:47 +0900 Subject: [PATCH 44/46] Fix a couple of remaining unnecessary casts --- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- osu.Game/Screens/Play/Spectator.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 697ceacf0a..dae27f35ae 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Dashboard var request = new GetUserRequest(u); request.Success += user => Schedule(() => { - if (playingUsers.Contains((int)user.Id)) + if (playingUsers.Contains(user.Id)) userFlow.Add(createUserPanel(user)); }); api.Queue(request); diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 9ed911efd5..0f593db277 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Play spectatorStreaming.OnUserFinishedPlaying += userFinishedPlaying; spectatorStreaming.OnNewFrames += userSentFrames; - spectatorStreaming.WatchUser((int)targetUser.Id); + spectatorStreaming.WatchUser(targetUser.Id); managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); @@ -353,7 +353,7 @@ namespace osu.Game.Screens.Play spectatorStreaming.OnUserFinishedPlaying -= userFinishedPlaying; spectatorStreaming.OnNewFrames -= userSentFrames; - spectatorStreaming.StopWatchingUser((int)targetUser.Id); + spectatorStreaming.StopWatchingUser(targetUser.Id); } managerUpdated?.UnbindAll(); From 74ca2faa31805e8a2aeac5def1170ab7945957d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:48:06 +0900 Subject: [PATCH 45/46] Remove unused using --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 3ca0b47121..be278622b5 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; From 517a65689964581396b85427ab4100eb87ae4cab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Nov 2020 13:51:25 +0900 Subject: [PATCH 46/46] Move StarDifficulty to own file --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 46 ------------------ osu.Game/Beatmaps/StarDifficulty.cs | 53 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 46 deletions(-) create mode 100644 osu.Game/Beatmaps/StarDifficulty.cs diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index be278622b5..af1b1de0c1 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -16,7 +16,6 @@ using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Database; using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -344,49 +343,4 @@ namespace osu.Game.Beatmaps } } } - - public readonly struct StarDifficulty - { - /// - /// The star difficulty rating for the given beatmap. - /// - public readonly double Stars; - - /// - /// The maximum combo achievable on the given beatmap. - /// - public readonly int MaxCombo; - - /// - /// The difficulty attributes computed for the given beatmap. - /// Might not be available if the star difficulty is associated with a beatmap that's not locally available. - /// - [CanBeNull] - public readonly DifficultyAttributes Attributes; - - /// - /// Creates a structure based on computed - /// by a . - /// - public StarDifficulty([NotNull] DifficultyAttributes attributes) - { - Stars = attributes.StarRating; - MaxCombo = attributes.MaxCombo; - Attributes = attributes; - // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...) - } - - /// - /// Creates a structure with a pre-populated star difficulty and max combo - /// in scenarios where computing is not feasible (i.e. when working with online sources). - /// - public StarDifficulty(double starDifficulty, int maxCombo) - { - Stars = starDifficulty; - MaxCombo = maxCombo; - Attributes = null; - } - - public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(Stars); - } } diff --git a/osu.Game/Beatmaps/StarDifficulty.cs b/osu.Game/Beatmaps/StarDifficulty.cs new file mode 100644 index 0000000000..f438b6f0bc --- /dev/null +++ b/osu.Game/Beatmaps/StarDifficulty.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Game.Rulesets.Difficulty; + +namespace osu.Game.Beatmaps +{ + public readonly struct StarDifficulty + { + /// + /// The star difficulty rating for the given beatmap. + /// + public readonly double Stars; + + /// + /// The maximum combo achievable on the given beatmap. + /// + public readonly int MaxCombo; + + /// + /// The difficulty attributes computed for the given beatmap. + /// Might not be available if the star difficulty is associated with a beatmap that's not locally available. + /// + [CanBeNull] + public readonly DifficultyAttributes Attributes; + + /// + /// Creates a structure based on computed + /// by a . + /// + public StarDifficulty([NotNull] DifficultyAttributes attributes) + { + Stars = attributes.StarRating; + MaxCombo = attributes.MaxCombo; + Attributes = attributes; + // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...) + } + + /// + /// Creates a structure with a pre-populated star difficulty and max combo + /// in scenarios where computing is not feasible (i.e. when working with online sources). + /// + public StarDifficulty(double starDifficulty, int maxCombo) + { + Stars = starDifficulty; + MaxCombo = maxCombo; + Attributes = null; + } + + public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(Stars); + } +}