diff --git a/osu.Android.props b/osu.Android.props
index 301c615ce4..3cd4dc48bf 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -54,6 +54,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
index c721ff862a..46e427e1b7 100644
--- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
@@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils
{
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF;
- private const uint y = 842502087;
- private const uint z = 3579807591;
- private const uint w = 273326509;
- private uint _x, _y = y, _z = z, _w = w;
+ private const uint y_initial = 842502087;
+ private const uint z_initial = 3579807591;
+ private const uint w_initial = 273326509;
+ private uint x, y = y_initial, z = z_initial, w = w_initial;
public FastRandom(int seed)
{
- _x = (uint)seed;
+ x = (uint)seed;
}
public FastRandom()
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils
/// The random value.
public uint NextUInt()
{
- uint t = _x ^ (_x << 11);
- _x = _y;
- _y = _z;
- _z = _w;
- return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8);
+ uint t = x ^ (x << 11);
+ x = y;
+ y = z;
+ z = w;
+ return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}
///
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
index e3c6c93d01..025fa9c56e 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle")
+ InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle", confineMode: ConfineMode.ScaleDownToFit)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index e45dc1d47a..c7db40bb19 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -136,8 +136,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
slider.Path.ControlPoints.Remove(c);
}
- // If there are 0 remaining control points, treat the slider as being deleted
- if (slider.Path.ControlPoints.Count == 0)
+ // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
+ if (slider.Path.ControlPoints.Count <= 1)
{
placementHandler?.Delete(slider);
return true;
@@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return new MenuItem[]
{
- new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected())
+ new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints, selectedPoints > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected())
};
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index c004b6db28..9b820261ab 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -191,15 +190,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
Initial,
Body,
}
-
- private class Segment
- {
- public readonly List ControlPoints = new List();
-
- public Segment(Vector2 offset)
- {
- ControlPoints.Add(offset);
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index 7431972673..68873093a6 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
int insertionIndex = 0;
float minDistance = float.MaxValue;
- for (int i = 0; i < HitObject.Path.ControlPoints.Count - 2; i++)
+ for (int i = 0; i < HitObject.Path.ControlPoints.Count - 1; i++)
{
float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position);
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
index 05b38ae195..02152fa51e 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
@@ -3,15 +3,14 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
+using osu.Game.Rulesets.Osu.UI.Cursor;
using osuTK;
namespace osu.Game.Rulesets.Osu.Skinning
{
- public class LegacyCursor : CompositeDrawable
+ public class LegacyCursor : OsuCursorSprite
{
- private NonPlayfieldSprite cursor;
private bool spin;
public LegacyCursor()
@@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
- InternalChildren = new Drawable[]
+ InternalChildren = new[]
{
new NonPlayfieldSprite
{
@@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
- cursor = new NonPlayfieldSprite
+ ExpandTarget = new NonPlayfieldSprite
{
Texture = skin.GetTexture("cursor"),
Anchor = Anchor.Centre,
@@ -47,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
protected override void LoadComplete()
{
if (spin)
- cursor.Spin(10000, RotationDirection.Clockwise);
+ ExpandTarget.Spin(10000, RotationDirection.Clockwise);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
index 0aa8661fd3..4f3d07f208 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
@@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private bool cursorExpand;
- private Container expandTarget;
+ private SkinnableDrawable cursorSprite;
+
+ private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite;
public OsuCursor()
{
@@ -37,12 +39,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = expandTarget = new Container
+ InternalChild = new Container
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
+ Child = cursorSprite = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
- private class DefaultCursor : CompositeDrawable
+ private class DefaultCursor : OsuCursorSprite
{
public DefaultCursor()
{
@@ -71,10 +73,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- InternalChildren = new Drawable[]
+ InternalChildren = new[]
{
- new CircularContainer
+ ExpandTarget = new CircularContainer
{
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = size / 6,
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs
new file mode 100644
index 0000000000..573c408a78
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Rulesets.Osu.UI.Cursor
+{
+ public abstract class OsuCursorSprite : CompositeDrawable
+ {
+ ///
+ /// The an optional piece of the cursor to expand when in a clicked state.
+ /// If null, the whole cursor will be affected by expansion.
+ ///
+ public Drawable ExpandTarget { get; protected set; }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index dbea8d28a6..f02361e685 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.Gameplay
beforeLoadAction?.Invoke();
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
- foreach (var mod in Mods.Value.OfType())
- mod.ApplyToClock(Beatmap.Value.Track);
+ foreach (var mod in Mods.Value.OfType())
+ mod.ApplyToTrack(Beatmap.Value.Track);
InputManager.Child = container = new TestPlayerLoaderContainer(
loader = new TestPlayerLoader(() =>
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
new file mode 100644
index 0000000000..e3dae9c27e
--- /dev/null
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
@@ -0,0 +1,65 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Configuration;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Settings
+{
+ [TestFixture]
+ public class TestSceneSettingsSource : OsuTestScene
+ {
+ public TestSceneSettingsSource()
+ {
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(20),
+ Width = 0.5f,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Padding = new MarginPadding(50),
+ ChildrenEnumerable = new TestTargetClass().CreateSettingsControls()
+ },
+ };
+ }
+
+ private class TestTargetClass
+ {
+ [SettingSource("Sample bool", "Clicking this changes a setting")]
+ public BindableBool TickBindable { get; } = new BindableBool();
+
+ [SettingSource("Sample float", "Change something for a mod")]
+ public BindableFloat SliderBindable { get; } = new BindableFloat
+ {
+ MinValue = 0,
+ MaxValue = 10,
+ Default = 5,
+ Value = 7
+ };
+
+ [SettingSource("Sample enum", "Change something for a mod")]
+ public Bindable EnumBindable { get; } = new Bindable
+ {
+ Default = TestEnum.Value1,
+ Value = TestEnum.Value2
+ };
+
+ [SettingSource("Sample string", "Change something for a mod")]
+ public Bindable StringBindable { get; } = new Bindable();
+ }
+
+ private enum TestEnum
+ {
+ Value1,
+ Value2
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index a4b8d1a24a..5dd02c1ddd 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -95,6 +95,42 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
}
+ [Test]
+ public void TestNoFilterOnSimpleResume()
+ {
+ addRulesetImportStep(0);
+ addRulesetImportStep(0);
+
+ createSongSelect();
+
+ AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+
+ AddStep("return", () => songSelect.MakeCurrent());
+ AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
+ AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
+ }
+
+ [Test]
+ public void TestFilterOnResumeAfterChange()
+ {
+ addRulesetImportStep(0);
+ addRulesetImportStep(0);
+
+ AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false));
+
+ createSongSelect();
+
+ AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
+ AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
+
+ AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
+
+ AddStep("return", () => songSelect.MakeCurrent());
+ AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
+ AddAssert("filter count is 2", () => songSelect.FilterCount == 2);
+ }
+
[Test]
public void TestAudioResuming()
{
diff --git a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
index a68fd0ef40..c55988d1bb 100644
--- a/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
+++ b/osu.Game.Tests/Visual/TestSceneOsuScreenStack.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
}
- private class TestScreen : ScreenWithBeatmapBackground
+ public class TestScreen : ScreenWithBeatmapBackground
{
private readonly string screenText;
diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs
index a345f93896..3ff4718b75 100644
--- a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs
+++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs
@@ -83,88 +83,81 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
};
}
- private ScrollState _scrollState;
+ private ScrollState scrollState;
- private ScrollState scrollState
+ private void setScrollState(ScrollState newstate)
{
- get => _scrollState;
+ if (scrollState == newstate)
+ return;
- set
+ delayedStateChangeDelegate?.Cancel();
+
+ switch (scrollState = newstate)
{
- if (_scrollState == value)
- return;
+ case ScrollState.Scrolling:
+ resetSelected();
- _scrollState = value;
+ OnScrollStarted?.Invoke();
- delayedStateChangeDelegate?.Cancel();
+ speedTo(1000f, 200);
+ tracker.FadeOut(100);
+ break;
- switch (value)
- {
- case ScrollState.Scrolling:
- resetSelected();
+ case ScrollState.Stopping:
+ speedTo(0f, 2000);
+ tracker.FadeIn(200);
- OnScrollStarted?.Invoke();
+ delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Stopped), 2300);
+ break;
- speedTo(1000f, 200);
- tracker.FadeOut(100);
+ case ScrollState.Stopped:
+ // Find closest to center
+ if (!Children.Any())
break;
- case ScrollState.Stopping:
- speedTo(0f, 2000);
- tracker.FadeIn(200);
+ ScrollingTeam closest = null;
- delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Stopped, 2300);
- break;
+ foreach (var c in Children)
+ {
+ if (!(c is ScrollingTeam stc))
+ continue;
- case ScrollState.Stopped:
- // Find closest to center
- if (!Children.Any())
- break;
-
- ScrollingTeam closest = null;
-
- foreach (var c in Children)
+ if (closest == null)
{
- if (!(c is ScrollingTeam stc))
- continue;
-
- if (closest == null)
- {
- closest = stc;
- continue;
- }
-
- float o = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f);
- float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f);
-
- if (o < lastOffset)
- closest = stc;
+ closest = stc;
+ continue;
}
- Trace.Assert(closest != null, "closest != null");
+ float o = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f);
+ float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f);
- // ReSharper disable once PossibleNullReferenceException
- offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
+ if (o < lastOffset)
+ closest = stc;
+ }
- ScrollingTeam st = closest;
+ Trace.Assert(closest != null, "closest != null");
- availableTeams.RemoveAll(at => at == st.Team);
+ // ReSharper disable once PossibleNullReferenceException
+ offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
- st.Selected = true;
- OnSelected?.Invoke(st.Team);
+ ScrollingTeam st = closest;
- delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Idle, 10000);
- break;
+ availableTeams.RemoveAll(at => at == st.Team);
- case ScrollState.Idle:
- resetSelected();
+ st.Selected = true;
+ OnSelected?.Invoke(st.Team);
- OnScrollStarted?.Invoke();
+ delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Idle), 10000);
+ break;
- speedTo(40f, 200);
- tracker.FadeOut(100);
- break;
- }
+ case ScrollState.Idle:
+ resetSelected();
+
+ OnScrollStarted?.Invoke();
+
+ speedTo(40f, 200);
+ tracker.FadeOut(100);
+ break;
}
}
@@ -176,7 +169,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
availableTeams.Add(team);
RemoveAll(c => c is ScrollingTeam);
- scrollState = ScrollState.Idle;
+ setScrollState(ScrollState.Idle);
}
public void AddTeams(IEnumerable teams)
@@ -192,7 +185,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
{
availableTeams.Clear();
RemoveAll(c => c is ScrollingTeam);
- scrollState = ScrollState.Idle;
+ setScrollState(ScrollState.Idle);
}
public void RemoveTeam(TournamentTeam team)
@@ -217,7 +210,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
if (availableTeams.Count == 0)
return;
- scrollState = ScrollState.Scrolling;
+ setScrollState(ScrollState.Scrolling);
}
public void StopScrolling()
@@ -232,13 +225,13 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
return;
}
- scrollState = ScrollState.Stopping;
+ setScrollState(ScrollState.Stopping);
}
protected override void LoadComplete()
{
base.LoadComplete();
- scrollState = ScrollState.Idle;
+ setScrollState(ScrollState.Idle);
}
protected override void UpdateAfterChildren()
@@ -305,7 +298,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
private void speedTo(float value, double duration = 0, Easing easing = Easing.None) =>
this.TransformTo(nameof(speed), value, duration, easing);
- private enum ScrollState
+ protected enum ScrollState
{
None,
Idle,
diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs
index 1ef4c2527a..b3783b45a8 100644
--- a/osu.Game/Configuration/DatabasedConfigManager.cs
+++ b/osu.Game/Configuration/DatabasedConfigManager.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
@@ -9,8 +10,8 @@ using osu.Game.Rulesets;
namespace osu.Game.Configuration
{
- public abstract class DatabasedConfigManager : ConfigManager
- where T : struct
+ public abstract class DatabasedConfigManager : ConfigManager
+ where TLookup : struct, Enum
{
private readonly SettingsStore settings;
@@ -53,7 +54,7 @@ namespace osu.Game.Configuration
private readonly List dirtySettings = new List();
- protected override void AddBindable(T lookup, Bindable bindable)
+ protected override void AddBindable(TLookup lookup, Bindable bindable)
{
base.AddBindable(lookup, bindable);
diff --git a/osu.Game/Configuration/InMemoryConfigManager.cs b/osu.Game/Configuration/InMemoryConfigManager.cs
index b0dc6b0e9c..ccf697f680 100644
--- a/osu.Game/Configuration/InMemoryConfigManager.cs
+++ b/osu.Game/Configuration/InMemoryConfigManager.cs
@@ -1,12 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Configuration;
namespace osu.Game.Configuration
{
- public class InMemoryConfigManager : ConfigManager
- where T : struct
+ public class InMemoryConfigManager : ConfigManager
+ where TLookup : struct, Enum
{
public InMemoryConfigManager()
{
diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs
new file mode 100644
index 0000000000..056fa8bcc0
--- /dev/null
+++ b/osu.Game/Configuration/SettingSourceAttribute.cs
@@ -0,0 +1,110 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using JetBrains.Annotations;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Overlays.Settings;
+
+namespace osu.Game.Configuration
+{
+ ///
+ /// An attribute to mark a bindable as being exposed to the user via settings controls.
+ /// Can be used in conjunction with to automatically create UI controls.
+ ///
+ [MeansImplicitUse]
+ [AttributeUsage(AttributeTargets.Property)]
+ public class SettingSourceAttribute : Attribute
+ {
+ public string Label { get; }
+
+ public string Description { get; }
+
+ public SettingSourceAttribute(string label, string description = null)
+ {
+ Label = label ?? string.Empty;
+ Description = description ?? string.Empty;
+ }
+ }
+
+ public static class SettingSourceExtensions
+ {
+ public static IEnumerable CreateSettingsControls(this object obj)
+ {
+ foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
+ {
+ var attr = property.GetCustomAttribute(true);
+
+ if (attr == null)
+ continue;
+
+ var prop = property.GetValue(obj);
+
+ switch (prop)
+ {
+ case BindableNumber bNumber:
+ yield return new SettingsSlider
+ {
+ LabelText = attr.Label,
+ Bindable = bNumber
+ };
+
+ break;
+
+ case BindableNumber bNumber:
+ yield return new SettingsSlider
+ {
+ LabelText = attr.Label,
+ Bindable = bNumber
+ };
+
+ break;
+
+ case BindableNumber bNumber:
+ yield return new SettingsSlider
+ {
+ LabelText = attr.Label,
+ Bindable = bNumber
+ };
+
+ break;
+
+ case Bindable bBool:
+ yield return new SettingsCheckbox
+ {
+ LabelText = attr.Label,
+ Bindable = bBool
+ };
+
+ break;
+
+ case Bindable bString:
+ yield return new SettingsTextBox
+ {
+ LabelText = attr.Label,
+ Bindable = bString
+ };
+
+ break;
+
+ case IBindable bindable:
+ var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
+ var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
+
+ dropdown.GetType().GetProperty(nameof(IHasCurrentValue
/// A to which further transforms can be added.
public static TransformSequence FadeAccent(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
- where T : IHasAccentColour
+ where T : class, IHasAccentColour
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
///
diff --git a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs
index e132027787..528d7d60f8 100644
--- a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs
@@ -6,12 +6,10 @@ using System;
namespace osu.Game.Graphics.UserInterface
{
public class OsuEnumDropdown : OsuDropdown
+ where T : struct, Enum
{
public OsuEnumDropdown()
{
- if (!typeof(T).IsEnum)
- throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
-
Items = (T[])Enum.GetValues(typeof(T));
}
}
diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs
index dcec17788a..9a0e112727 100644
--- a/osu.Game/Online/DownloadTrackingComposite.cs
+++ b/osu.Game/Online/DownloadTrackingComposite.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Online
///
protected readonly Bindable State = new Bindable();
- protected readonly Bindable Progress = new Bindable();
+ protected readonly BindableNumber Progress = new BindableNumber { MinValue = 0, MaxValue = 1 };
protected DownloadTrackingComposite(TModel model = null)
{
diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs
index e47d497d94..d13e8b31e6 100644
--- a/osu.Game/Online/Multiplayer/PlaylistItem.cs
+++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs
@@ -45,23 +45,25 @@ namespace osu.Game.Online.Multiplayer
[JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; }
+ private APIMod[] allowedModsBacking;
+
[JsonProperty("allowed_mods")]
private APIMod[] allowedMods
{
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
- set => _allowedMods = value;
+ set => allowedModsBacking = value;
}
+ private APIMod[] requiredModsBacking;
+
[JsonProperty("required_mods")]
private APIMod[] requiredMods
{
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
- set => _requiredMods = value;
+ set => requiredModsBacking = value;
}
private BeatmapInfo beatmap;
- private APIMod[] _allowedMods;
- private APIMod[] _requiredMods;
public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets)
{
@@ -70,20 +72,20 @@ namespace osu.Game.Online.Multiplayer
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
Ruleset = rulesets.GetRuleset(RulesetID);
- if (_allowedMods != null)
+ if (allowedModsBacking != null)
{
AllowedMods.Clear();
- AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym)));
+ AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym)));
- _allowedMods = null;
+ allowedModsBacking = null;
}
- if (_requiredMods != null)
+ if (requiredModsBacking != null)
{
RequiredMods.Clear();
- RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym)));
+ RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym)));
- _requiredMods = null;
+ requiredModsBacking = null;
}
}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 5e0a67c2f7..bafdad3508 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -261,8 +261,8 @@ namespace osu.Game.Overlays
if (allowRateAdjustments)
{
- foreach (var mod in mods.Value.OfType())
- mod.ApplyToClock(track);
+ foreach (var mod in mods.Value.OfType())
+ mod.ApplyToTrack(track);
}
}
diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
index 0808cc8fcc..a33f4eb30d 100644
--- a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
+++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Bindables;
using osuTK;
using osu.Framework.Graphics;
@@ -11,6 +12,7 @@ using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public class DisplayStyleControl : Container
+ where T : struct, Enum
{
public readonly SlimEnumDropdown Dropdown;
public readonly Bindable DisplayStyle = new Bindable();
diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
index 372da94b37..117f905de4 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
@@ -13,7 +13,9 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
- public abstract class SearchableListFilterControl : Container
+ public abstract class SearchableListFilterControl : Container
+ where TTab : struct, Enum
+ where TCategory : struct, Enum
{
private const float padding = 10;
@@ -21,12 +23,12 @@ namespace osu.Game.Overlays.SearchableList
private readonly Box tabStrip;
public readonly SearchTextBox Search;
- public readonly PageTabControl Tabs;
- public readonly DisplayStyleControl DisplayStyleControl;
+ public readonly PageTabControl Tabs;
+ public readonly DisplayStyleControl DisplayStyleControl;
protected abstract Color4 BackgroundColour { get; }
- protected abstract T DefaultTab { get; }
- protected abstract U DefaultCategory { get; }
+ protected abstract TTab DefaultTab { get; }
+ protected abstract TCategory DefaultCategory { get; }
protected virtual Drawable CreateSupplementaryControls() => null;
///
@@ -36,9 +38,6 @@ namespace osu.Game.Overlays.SearchableList
protected SearchableListFilterControl()
{
- if (!typeof(T).IsEnum)
- throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
-
RelativeSizeAxes = Axes.X;
var controls = CreateSupplementaryControls();
@@ -90,7 +89,7 @@ namespace osu.Game.Overlays.SearchableList
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Right = 225 },
- Child = Tabs = new PageTabControl
+ Child = Tabs = new PageTabControl
{
RelativeSizeAxes = Axes.X,
},
@@ -105,7 +104,7 @@ namespace osu.Game.Overlays.SearchableList
},
},
},
- DisplayStyleControl = new DisplayStyleControl
+ DisplayStyleControl = new DisplayStyleControl
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs
index 73dca956d1..66fedf0a56 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs
@@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListHeader : Container
+ where T : struct, Enum
{
public readonly HeaderTabControl Tabs;
@@ -24,9 +25,6 @@ namespace osu.Game.Overlays.SearchableList
protected SearchableListHeader()
{
- if (!typeof(T).IsEnum)
- throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument");
-
RelativeSizeAxes = Axes.X;
Height = 90;
diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
index fb0c1d9808..37478d902b 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -16,19 +17,22 @@ namespace osu.Game.Overlays.SearchableList
public const float WIDTH_PADDING = 80;
}
- public abstract class SearchableListOverlay : SearchableListOverlay
+ public abstract class SearchableListOverlay : SearchableListOverlay
+ where THeader : struct, Enum
+ where TTab : struct, Enum
+ where TCategory : struct, Enum
{
private readonly Container scrollContainer;
- protected readonly SearchableListHeader Header;
- protected readonly SearchableListFilterControl Filter;
+ protected readonly SearchableListHeader Header;
+ protected readonly SearchableListFilterControl Filter;
protected readonly FillFlowContainer ScrollFlow;
protected abstract Color4 BackgroundColour { get; }
protected abstract Color4 TrianglesColourLight { get; }
protected abstract Color4 TrianglesColourDark { get; }
- protected abstract SearchableListHeader CreateHeader();
- protected abstract SearchableListFilterControl CreateFilterControl();
+ protected abstract SearchableListHeader CreateHeader();
+ protected abstract SearchableListFilterControl CreateFilterControl();
protected SearchableListOverlay()
{
diff --git a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs
index f320ef1344..9e7ff1205f 100644
--- a/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs
+++ b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -11,6 +12,7 @@ using osuTK;
namespace osu.Game.Overlays.SearchableList
{
public class SlimEnumDropdown : OsuEnumDropdown
+ where T : struct, Enum
{
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
index 9f09f251c2..c77d14632b 100644
--- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
+++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
@@ -1,12 +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.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public class SettingsEnumDropdown : SettingsDropdown
+ where T : struct, Enum
{
protected override OsuDropdown CreateDropdown() => new DropdownControl();
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index 8863e43cca..9c390c34ec 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -53,27 +53,10 @@ namespace osu.Game.Overlays.Settings
}
}
- // hold a reference to the provided bindable so we don't have to in every settings section.
- private Bindable bindable;
-
public virtual Bindable Bindable
{
- get => bindable;
-
- set
- {
- if (bindable != null)
- controlWithCurrent?.Current.UnbindFrom(bindable);
-
- bindable = value;
- controlWithCurrent?.Current.BindTo(bindable);
-
- if (ShowsDefaultIndicator)
- {
- restoreDefaultButton.Bindable = bindable.GetBoundCopy();
- restoreDefaultButton.Bindable.TriggerChange();
- }
- }
+ get => controlWithCurrent.Current;
+ set => controlWithCurrent.Current = value;
}
public virtual IEnumerable FilterTerms => Keywords == null ? new[] { LabelText } : new List(Keywords) { LabelText }.ToArray();
@@ -110,7 +93,12 @@ namespace osu.Game.Overlays.Settings
private void load()
{
if (controlWithCurrent != null)
+ {
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
+
+ if (ShowsDefaultIndicator)
+ restoreDefaultButton.Bindable = controlWithCurrent.Current;
+ }
}
private class RestoreDefaultValueButton : Container, IHasTooltip
@@ -125,6 +113,7 @@ namespace osu.Game.Overlays.Settings
bindable = value;
bindable.ValueChanged += _ => UpdateState();
bindable.DisabledChanged += _ => UpdateState();
+ UpdateState();
}
}
diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
index ed5fdf9809..0ff3455f00 100644
--- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
+++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
@@ -1,12 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Game.Configuration;
namespace osu.Game.Rulesets.Configuration
{
- public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager
- where T : struct
+ public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager
+ where TLookup : struct, Enum
{
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index e31c963403..1902de5bda 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -4,8 +4,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
@@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Difficulty
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
- var clock = new StopwatchClock();
- mods.OfType().ForEach(m => m.ApplyToClock(clock));
+ var track = new TrackVirtual(10000);
+ mods.OfType().ForEach(m => m.ApplyToTrack(track));
- return calculate(playableBeatmap, mods, clock.Rate);
+ return calculate(playableBeatmap, mods, track.Rate);
}
///
diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs
index 9ab81b9580..ac3b817840 100644
--- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs
@@ -3,8 +3,8 @@
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
@@ -35,9 +35,9 @@ namespace osu.Game.Rulesets.Difficulty
protected virtual void ApplyMods(Mod[] mods)
{
- var clock = new StopwatchClock();
- mods.OfType().ForEach(m => m.ApplyToClock(clock));
- TimeRate = clock.Rate;
+ var track = new TrackVirtual(10000);
+ mods.OfType().ForEach(m => m.ApplyToTrack(track));
+ TimeRate = track.Rate;
}
public abstract double Calculate(Dictionary categoryDifficulty = null);
diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs
similarity index 69%
rename from osu.Game/Rulesets/Mods/IApplicableToClock.cs
rename to osu.Game/Rulesets/Mods/IApplicableToTrack.cs
index e5767b5fbf..4d6d958e82 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs
@@ -1,15 +1,15 @@
// 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.Timing;
+using osu.Framework.Audio.Track;
namespace osu.Game.Rulesets.Mods
{
///
/// An interface for mods that make adjustments to the track.
///
- public interface IApplicableToClock : IApplicableMod
+ public interface IApplicableToTrack : IApplicableMod
{
- void ApplyToClock(IAdjustableClock clock);
+ void ApplyToTrack(Track track);
}
}
diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs
index 7e6d959119..dcb3cb5597 100644
--- a/osu.Game/Rulesets/Mods/ModDaycore.cs
+++ b/osu.Game/Rulesets/Mods/ModDaycore.cs
@@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Timing;
namespace osu.Game.Rulesets.Mods
{
@@ -14,12 +13,9 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.Question;
public override string Description => "Whoaaaaa...";
- public override void ApplyToClock(IAdjustableClock clock)
+ public override void ApplyToTrack(Track track)
{
- if (clock is IHasPitchAdjust pitchAdjust)
- pitchAdjust.PitchAdjust *= RateAdjust;
- else
- base.ApplyToClock(clock);
+ track.Frequency.Value *= RateAdjust;
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
index a5e76e32b1..5e685b040e 100644
--- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs
+++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
@@ -8,7 +8,7 @@ using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModDoubleTime : ModTimeAdjust, IApplicableToClock
+ public abstract class ModDoubleTime : ModTimeAdjust
{
public override string Name => "Double Time";
public override string Acronym => "DT";
diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs
index 27369f4c30..d17ddd2253 100644
--- a/osu.Game/Rulesets/Mods/ModHalfTime.cs
+++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs
@@ -8,7 +8,7 @@ using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHalfTime : ModTimeAdjust, IApplicableToClock
+ public abstract class ModHalfTime : ModTimeAdjust
{
public override string Name => "Half Time";
public override string Acronym => "HT";
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index dc0fc33088..a4f1ef5a72 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Timing;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
@@ -15,12 +14,9 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => OsuIcon.ModNightcore;
public override string Description => "Uguuuuuuuu...";
- public override void ApplyToClock(IAdjustableClock clock)
+ public override void ApplyToTrack(Track track)
{
- if (clock is IHasPitchAdjust pitchAdjust)
- pitchAdjust.PitchAdjust *= RateAdjust;
- else
- base.ApplyToClock(clock);
+ track.Frequency.Value *= RateAdjust;
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs
index 513883f552..7d0cc2a7c3 100644
--- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs
+++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs
@@ -2,23 +2,19 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Audio;
-using osu.Framework.Timing;
+using osu.Framework.Audio.Track;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeAdjust : Mod
+ public abstract class ModTimeAdjust : Mod, IApplicableToTrack
{
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
protected abstract double RateAdjust { get; }
- public virtual void ApplyToClock(IAdjustableClock clock)
+ public virtual void ApplyToTrack(Track track)
{
- if (clock is IHasTempoAdjust tempo)
- tempo.TempoAdjust *= RateAdjust;
- else
- clock.Rate *= RateAdjust;
+ track.Tempo.Value *= RateAdjust;
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
index e231225e3c..839b2ae36e 100644
--- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs
+++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
@@ -3,15 +3,14 @@
using System;
using System.Linq;
-using osu.Framework.Audio;
-using osu.Framework.Timing;
+using osu.Framework.Audio.Track;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap
+ public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap
{
///
/// The point in the beatmap at which the final ramping rate should be reached.
@@ -24,11 +23,11 @@ namespace osu.Game.Rulesets.Mods
private double finalRateTime;
private double beginRampTime;
- private IAdjustableClock clock;
+ private Track track;
- public virtual void ApplyToClock(IAdjustableClock clock)
+ public virtual void ApplyToTrack(Track track)
{
- this.clock = clock;
+ this.track = track;
lastAdjust = 1;
@@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mods
public virtual void Update(Playfield playfield)
{
- applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime);
+ applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime);
}
private double lastAdjust = 1;
@@ -59,23 +58,8 @@ namespace osu.Game.Rulesets.Mods
{
double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment));
- switch (clock)
- {
- case IHasPitchAdjust pitch:
- pitch.PitchAdjust /= lastAdjust;
- pitch.PitchAdjust *= adjust;
- break;
-
- case IHasTempoAdjust tempo:
- tempo.TempoAdjust /= lastAdjust;
- tempo.TempoAdjust *= adjust;
- break;
-
- default:
- clock.Rate /= lastAdjust;
- clock.Rate *= adjust;
- break;
- }
+ track.Tempo.Value /= lastAdjust;
+ track.Tempo.Value *= adjust;
lastAdjust = adjust;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 1ac7284772..b5b1e26486 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -257,14 +257,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
if (type == PathType.PerfectCurve)
{
- if (vertices.Length == 3)
+ if (vertices.Length != 3)
+ type = PathType.Bezier;
+ else if (isLinear(vertices))
{
// osu-stable special-cased colinear perfect curves to a linear path
- if (isLinear(vertices))
- type = PathType.Linear;
+ type = PathType.Linear;
}
- else
- type = PathType.Bezier;
}
var points = new List(vertices.Length)
diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs
index 5737d3f618..0336f94313 100644
--- a/osu.Game/Rulesets/Objects/PathControlPoint.cs
+++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs
@@ -30,8 +30,9 @@ namespace osu.Game.Rulesets.Objects
/// Creates a new .
///
public PathControlPoint()
- : this(Vector2.Zero, null)
{
+ Position.ValueChanged += _ => Changed?.Invoke();
+ Type.ValueChanged += _ => Changed?.Invoke();
}
///
@@ -40,12 +41,10 @@ namespace osu.Game.Rulesets.Objects
/// The initial position.
/// The initial type.
public PathControlPoint(Vector2 position, PathType? type = null)
+ : this()
{
Position.Value = position;
Type.Value = type;
-
- Position.ValueChanged += _ => Changed?.Invoke();
- Type.ValueChanged += _ => Changed?.Invoke();
}
public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value;
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 96275c1274..a856974292 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -511,15 +511,19 @@ namespace osu.Game.Rulesets.UI
public IEnumerable GetAvailableResources() => throw new NotImplementedException();
- public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
+ public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException();
- public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
+ public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException();
- public BindableDouble Volume => throw new NotImplementedException();
+ public BindableNumber Volume => throw new NotImplementedException();
- public BindableDouble Balance => throw new NotImplementedException();
+ public BindableNumber Balance => throw new NotImplementedException();
- public BindableDouble Frequency => throw new NotImplementedException();
+ public BindableNumber Frequency => throw new NotImplementedException();
+
+ public BindableNumber Tempo => throw new NotImplementedException();
+
+ public IBindable GetAggregate(AdjustableProperty type) => throw new NotImplementedException();
public IBindable AggregateVolume => throw new NotImplementedException();
@@ -527,6 +531,8 @@ namespace osu.Game.Rulesets.UI
public IBindable AggregateFrequency => throw new NotImplementedException();
+ public IBindable AggregateTempo => throw new NotImplementedException();
+
public int PlaybackConcurrency
{
get => throw new NotImplementedException();
diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs
index 58c9a6a784..2cc03ae453 100644
--- a/osu.Game/Screens/Play/GameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/GameplayClockContainer.cs
@@ -28,9 +28,9 @@ namespace osu.Game.Screens.Play
private readonly IReadOnlyList mods;
///
- /// The original source (usually a 's track).
+ /// The 's track.
///
- private IAdjustableClock sourceClock;
+ private Track track;
public readonly BindableBool IsPaused = new BindableBool();
@@ -72,8 +72,8 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both;
- sourceClock = (IAdjustableClock)beatmap.Track ?? new StopwatchClock();
- (sourceClock as IAdjustableAudioComponent)?.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
+ track = beatmap.Track;
+ track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
@@ -127,11 +127,11 @@ namespace osu.Game.Screens.Play
{
Task.Run(() =>
{
- sourceClock.Reset();
+ track.Reset();
Schedule(() =>
{
- adjustableClock.ChangeSource(sourceClock);
+ adjustableClock.ChangeSource(track);
updateRate();
if (!IsPaused.Value)
@@ -197,13 +197,13 @@ namespace osu.Game.Screens.Play
///
public void StopUsingBeatmapClock()
{
- if (sourceClock != beatmap.Track)
+ if (track != beatmap.Track)
return;
removeSourceClockAdjustments();
- sourceClock = new TrackVirtual(beatmap.Track.Length);
- adjustableClock.ChangeSource(sourceClock);
+ track = new TrackVirtual(beatmap.Track.Length);
+ adjustableClock.ChangeSource(track);
}
protected override void Update()
@@ -218,18 +218,15 @@ namespace osu.Game.Screens.Play
private void updateRate()
{
- if (sourceClock == null) return;
+ if (track == null) return;
speedAdjustmentsApplied = true;
- sourceClock.ResetSpeedAdjustments();
+ track.ResetSpeedAdjustments();
- if (sourceClock is IHasTempoAdjust tempo)
- tempo.TempoAdjust = UserPlaybackRate.Value;
- else
- sourceClock.Rate = UserPlaybackRate.Value;
+ track.Tempo.Value = UserPlaybackRate.Value;
- foreach (var mod in mods.OfType())
- mod.ApplyToClock(sourceClock);
+ foreach (var mod in mods.OfType())
+ mod.ApplyToTrack(track);
}
protected override void Dispose(bool isDisposing)
@@ -237,18 +234,18 @@ namespace osu.Game.Screens.Play
base.Dispose(isDisposing);
removeSourceClockAdjustments();
- sourceClock = null;
+ track = null;
}
private void removeSourceClockAdjustments()
{
if (speedAdjustmentsApplied)
{
- sourceClock.ResetSpeedAdjustments();
+ track.ResetSpeedAdjustments();
speedAdjustmentsApplied = false;
}
- (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
+ track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
}
}
}
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index f54d638584..adfbe977a4 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -188,26 +188,22 @@ namespace osu.Game.Screens.Play
InternalButtons.Add(button);
}
- private int _selectionIndex = -1;
+ private int selectionIndex = -1;
- private int selectionIndex
+ private void setSelected(int value)
{
- get => _selectionIndex;
- set
- {
- if (_selectionIndex == value)
- return;
+ if (selectionIndex == value)
+ return;
- // Deselect the previously-selected button
- if (_selectionIndex != -1)
- InternalButtons[_selectionIndex].Selected.Value = false;
+ // Deselect the previously-selected button
+ if (selectionIndex != -1)
+ InternalButtons[selectionIndex].Selected.Value = false;
- _selectionIndex = value;
+ selectionIndex = value;
- // Select the newly-selected button
- if (_selectionIndex != -1)
- InternalButtons[_selectionIndex].Selected.Value = true;
- }
+ // Select the newly-selected button
+ if (selectionIndex != -1)
+ InternalButtons[selectionIndex].Selected.Value = true;
}
protected override bool OnKeyDown(KeyDownEvent e)
@@ -218,16 +214,16 @@ namespace osu.Game.Screens.Play
{
case Key.Up:
if (selectionIndex == -1 || selectionIndex == 0)
- selectionIndex = InternalButtons.Count - 1;
+ setSelected(InternalButtons.Count - 1);
else
- selectionIndex--;
+ setSelected(selectionIndex - 1);
return true;
case Key.Down:
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
- selectionIndex = 0;
+ setSelected(0);
else
- selectionIndex++;
+ setSelected(selectionIndex + 1);
return true;
}
}
@@ -266,9 +262,9 @@ namespace osu.Game.Screens.Play
private void buttonSelectionChanged(DialogButton button, bool isSelected)
{
if (!isSelected)
- selectionIndex = -1;
+ setSelected(-1);
else
- selectionIndex = InternalButtons.IndexOf(button);
+ setSelected(InternalButtons.IndexOf(button));
}
private void updateRetryCount()
diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs
index abcb1f2171..e3ad76ac35 100644
--- a/osu.Game/Screens/Select/FilterCriteria.cs
+++ b/osu.Game/Screens/Select/FilterCriteria.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select
}
public struct OptionalRange : IEquatable>
- where T : struct, IComparable
+ where T : struct
{
public bool HasFilter => Max != null || Min != null;
diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs
index ffe1258168..89afc729fe 100644
--- a/osu.Game/Screens/Select/FilterQueryParser.cs
+++ b/osu.Game/Screens/Select/FilterQueryParser.cs
@@ -170,7 +170,7 @@ namespace osu.Game.Screens.Select
}
private static void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, T value)
- where T : struct, IComparable
+ where T : struct
{
switch (op)
{
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index a52edb70db..8f7ad2022d 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -262,8 +262,10 @@ namespace osu.Game.Screens.Select
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
{
- if (this.IsCurrentScreen())
- Carousel.Filter(criteria);
+ // if not the current screen, we want to get carousel in a good presentation state before displaying (resume or enter).
+ bool shouldDebounce = this.IsCurrentScreen();
+
+ Schedule(() => Carousel.Filter(criteria, shouldDebounce));
}
private DependencyContainer dependencies;
@@ -437,8 +439,6 @@ namespace osu.Game.Screens.Select
{
base.OnEntering(last);
- Carousel.Filter(FilterControl.CreateCriteria(), false);
-
this.FadeInFromZero(250);
FilterControl.Activate();
}
diff --git a/osu.Game/Skinning/SkinConfigManager.cs b/osu.Game/Skinning/SkinConfigManager.cs
index 896444d1d2..682138a2e9 100644
--- a/osu.Game/Skinning/SkinConfigManager.cs
+++ b/osu.Game/Skinning/SkinConfigManager.cs
@@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Configuration;
namespace osu.Game.Skinning
{
- public class SkinConfigManager : ConfigManager where T : struct
+ public class SkinConfigManager : ConfigManager where TLookup : struct, Enum
{
protected override void PerformLoad()
{
diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs
index 9e12de5833..1c4cdde22d 100644
--- a/osu.Game/Storyboards/Drawables/IFlippable.cs
+++ b/osu.Game/Storyboards/Drawables/IFlippable.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables
///
/// A to which further transforms can be added.
public static TransformSequence TransformFlipH(this T flippable, bool newValue, double delay = 0)
- where T : IFlippable
+ where T : class, IFlippable
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay));
///
@@ -49,7 +49,7 @@ namespace osu.Game.Storyboards.Drawables
///
/// A to which further transforms can be added.
public static TransformSequence TransformFlipV(this T flippable, bool newValue, double delay = 0)
- where T : IFlippable
+ where T : class, IFlippable
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay));
}
}
diff --git a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs
index 921a1d9789..ad24ffc7b8 100644
--- a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs
+++ b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
base.Update();
// note that this will override any mod rate application
- Beatmap.Value.Track.TempoAdjust = Clock.Rate;
+ Beatmap.Value.Track.Tempo.Value = Clock.Rate;
}
}
}
diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs
index 23f45e0d0f..707aa61283 100644
--- a/osu.Game/Tests/Visual/ScreenTestScene.cs
+++ b/osu.Game/Tests/Visual/ScreenTestScene.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual
///
public abstract class ScreenTestScene : ManualInputManagerTestScene
{
- private readonly OsuScreenStack stack;
+ protected readonly OsuScreenStack Stack;
private readonly Container content;
@@ -22,16 +22,16 @@ namespace osu.Game.Tests.Visual
{
base.Content.AddRange(new Drawable[]
{
- stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
+ Stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
content = new Container { RelativeSizeAxes = Axes.Both }
});
}
protected void LoadScreen(OsuScreen screen)
{
- if (stack.CurrentScreen != null)
- stack.Exit();
- stack.Push(screen);
+ if (Stack.CurrentScreen != null)
+ Stack.Exit();
+ Stack.Push(screen);
}
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index ef16738908..530d62f583 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 5090190f28..fb753b8c6f 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -74,7 +74,7 @@
-
+
@@ -82,7 +82,7 @@
-
+