diff --git a/.editorconfig b/.editorconfig
index b5333ad8e7..8cdb92d11c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -176,8 +176,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
#Style - C# 8 features
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:silent
-csharp_style_prefer_index_operator = false:none
-csharp_style_prefer_range_operator = false:none
+csharp_style_prefer_index_operator = true:warning
+csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = false:none
#Supressing roslyn built-in analyzers
diff --git a/build/InspectCode.cake b/build/InspectCode.cake
index bd3fdf5f93..06c56dce87 100644
--- a/build/InspectCode.cake
+++ b/build/InspectCode.cake
@@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
-#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1"
+#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
diff --git a/global.json b/global.json
index 43bb34912a..6858d4044d 100644
--- a/global.json
+++ b/global.json
@@ -1,4 +1,9 @@
{
+ "sdk": {
+ "allowPrerelease": false,
+ "rollForward": "minor",
+ "version": "3.1.100"
+ },
"msbuild-sdks": {
"Microsoft.Build.Traversal": "2.0.24"
}
diff --git a/osu.Android.props b/osu.Android.props
index 252570a150..abb3cc8244 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -53,7 +53,7 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
index ab3c040b4e..74a9c05bf9 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
+ SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset);
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
index 0369b6db4e..1eb913e900 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
@@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Catch.Tests
RelativeSizeAxes = Axes.Both,
Children = new[]
{
- drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap, Array.Empty())
+ drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo))
}
});
@@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private void addToPlayfield(DrawableCatchHitObject drawable)
{
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
drawableRuleset.Playfield.Add(drawable);
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs
index f6d26addaa..8c3dfef39c 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using NUnit.Framework;
using osu.Game.Rulesets.Catch.Mods;
namespace osu.Game.Rulesets.Catch.Tests
@@ -12,9 +13,10 @@ namespace osu.Game.Rulesets.Catch.Tests
{
public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList();
- public TestSceneDrawableHitObjectsHidden()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Mods.Value = new[] { new CatchModHidden() };
- }
+ SelectedMods.Value = new[] { new CatchModHidden() };
+ });
}
}
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 71d68ace94..506fa23fa9 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch
{
public class CatchRuleset : Ruleset
{
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableCatchRuleset(this, beatmap, mods);
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index d5d99640af..a4ed966abb 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (value != null)
{
- path.ControlPoints.AddRange(value.ControlPoints);
+ path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index f5bddeb2cb..278ff97195 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
protected override bool UserScrollSpeedAdjustment => false;
- public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Down;
- TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
+ TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
}
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 9069c09ae4..6e4491de94 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
prevNoteTimes.RemoveAt(0);
prevNoteTimes.Add(newNoteTime);
- density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
+ density = (prevNoteTimes[^1] - prevNoteTimes[0]) / prevNoteTimes.Count;
}
private double lastTime;
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs
index 059cd39641..4f7ab87fad 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs
@@ -5,7 +5,7 @@ using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
-using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
protected override double StrainValueOf(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
- var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
+ var endTime = maniaCurrent.BaseObject.GetEndTime();
try
{
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs
index ed25173d38..bbbb93fd8b 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs
@@ -4,7 +4,7 @@
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
-using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
{
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
protected override double StrainValueOf(DifficultyHitObject current)
{
var maniaCurrent = (ManiaDifficultyHitObject)current;
- var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
+ var endTime = maniaCurrent.BaseObject.GetEndTime();
double holdFactor = 1.0; // Factor in case something else is held
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs
index 97d8aaa052..445df79f6f 100644
--- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs
+++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit
{
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
- public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableManiaEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
: base(ruleset, beatmap, mods)
{
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index 0bfe6f9517..1632b6a583 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
- protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
{
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index c74a292331..a96c79b40b 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania
{
public class ManiaRuleset : Ruleset
{
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableManiaRuleset(this, beatmap, mods);
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableManiaRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index 0607bf0abd..cf1970c28b 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Bindable configDirection = new Bindable();
- public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
BarLines = new BarLineGenerator(Beatmap).BarLines;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
index 64f353c4d9..098e277fff 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
var drawable = CreateDrawableHitCircle(circle, auto);
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
return drawable;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs
index 55c6b22146..21ebce8c23 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs
@@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
- public TestSceneHitCircleHidden()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Mods.Value = new[] { new OsuModHidden() };
- }
+ SelectedMods.Value = new[] { new OsuModHidden() };
+ });
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs
index 64e7632b1b..412effe176 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
+ SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
return base.CreatePlayer(ruleset);
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index a9d5c03517..e8386363be 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests
var drawable = CreateDrawableSlider(slider);
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
drawable.OnNewResult += onNewResult;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs
index 2a9c1d167b..d0ee1bddb5 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs
@@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
- public TestSceneSliderHidden()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Mods.Value = new[] { new OsuModHidden() };
- }
+ SelectedMods.Value = new[] { new OsuModHidden() };
+ });
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index 5f75cbabec..b6fc9821a4 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -286,11 +286,11 @@ namespace osu.Game.Rulesets.Osu.Tests
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
- private bool assertHeadMissTailTracked() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
+ private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
- private bool assertMidSliderJudgements() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great;
+ private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great;
- private bool assertMidSliderJudgementFail() => judgementResults[judgementResults.Count - 2].Type == HitResult.Miss;
+ private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss;
private ScoreAccessibleReplayPlayer currentPlayer;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
index 3ed3f3e981..f53b64c729 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++
};
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
index a0ab1908d6..dd863deed2 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
@@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
- public TestSceneSpinnerHidden()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Mods.Value = new[] { new OsuModHidden() };
- }
+ SelectedMods.Value = new[] { new OsuModHidden() };
+ });
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
new file mode 100644
index 0000000000..0fc441fec6
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
@@ -0,0 +1,75 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Lines;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ ///
+ /// A visualisation of the line between two s.
+ ///
+ public class PathControlPointConnectionPiece : CompositeDrawable
+ {
+ public PathControlPoint ControlPoint;
+
+ private readonly Path path;
+ private readonly Slider slider;
+
+ private IBindable sliderPosition;
+ private IBindable pathVersion;
+
+ public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
+ {
+ this.slider = slider;
+ ControlPoint = controlPoint;
+
+ Origin = Anchor.Centre;
+ AutoSizeAxes = Axes.Both;
+
+ InternalChild = path = new SmoothPath
+ {
+ Anchor = Anchor.Centre,
+ PathRadius = 1
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ sliderPosition = slider.PositionBindable.GetBoundCopy();
+ sliderPosition.BindValueChanged(_ => updateConnectingPath());
+
+ pathVersion = slider.Path.Version.GetBoundCopy();
+ pathVersion.BindValueChanged(_ => updateConnectingPath());
+
+ updateConnectingPath();
+ }
+
+ ///
+ /// Updates the path connecting this control point to the next one.
+ ///
+ private void updateConnectingPath()
+ {
+ Position = slider.StackedPosition + ControlPoint.Position.Value;
+
+ path.ClearVertices();
+
+ int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
+
+ if (index == 0 || index == slider.Path.ControlPoints.Count)
+ return;
+
+ path.AddVertex(Vector2.Zero);
+ path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
+
+ path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index c2aefac587..6a0730db91 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
@@ -19,6 +18,9 @@ using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
+ ///
+ /// A visualisation of a single in a .
+ ///
public class PathControlPointPiece : BlueprintPiece
{
public Action RequestSelection;
@@ -28,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public readonly PathControlPoint ControlPoint;
private readonly Slider slider;
- private readonly Path path;
private readonly Container marker;
private readonly Drawable markerRing;
@@ -39,12 +40,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private OsuColour colours { get; set; }
private IBindable sliderPosition;
- private IBindable pathVersion;
+ private IBindable controlPointPosition;
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
-
ControlPoint = controlPoint;
Origin = Anchor.Centre;
@@ -52,11 +52,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChildren = new Drawable[]
{
- path = new SmoothPath
- {
- Anchor = Anchor.Centre,
- PathRadius = 1
- },
marker = new Container
{
Anchor = Anchor.Centre,
@@ -96,20 +91,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy();
- sliderPosition.BindValueChanged(_ => updateDisplay());
+ sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
- pathVersion = slider.Path.Version.GetBoundCopy();
- pathVersion.BindValueChanged(_ => updateDisplay());
+ controlPointPosition = ControlPoint.Position.GetBoundCopy();
+ controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
- updateDisplay();
- }
-
- private void updateDisplay()
- {
updateMarkerDisplay();
- updateConnectingPath();
}
// The connecting path is excluded from positional input
@@ -189,26 +178,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
colour = Color4.White;
marker.Colour = colour;
}
-
- ///
- /// Updates the path connecting this control point to the previous one.
- ///
- private void updateConnectingPath()
- {
- path.ClearVertices();
-
- int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
-
- if (index == -1)
- return;
-
- if (++index != slider.Path.ControlPoints.Count)
- {
- path.AddVertex(Vector2.Zero);
- path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
- }
-
- path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
- }
}
}
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 cd19653a2e..6f583d7983 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
internal readonly Container Pieces;
+ private readonly Container connections;
+
private readonly Slider slider;
private readonly bool allowSelection;
@@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
RelativeSizeAxes = Axes.Both;
- InternalChild = Pieces = new Container { RelativeSizeAxes = Axes.Both };
+ InternalChildren = new Drawable[]
+ {
+ connections = new Container { RelativeSizeAxes = Axes.Both },
+ Pieces = new Container { RelativeSizeAxes = Axes.Both }
+ };
}
protected override void LoadComplete()
@@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
foreach (var point in controlPoints)
{
- var piece = new PathControlPointPiece(slider, point);
+ Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
+ {
+ if (allowSelection)
+ d.RequestSelection = selectPiece;
+ }));
- if (allowSelection)
- piece.RequestSelection = selectPiece;
-
- Pieces.Add(piece);
+ connections.Add(new PathControlPointConnectionPiece(slider, point));
}
}
private void removeControlPoints(IEnumerable controlPoints)
{
foreach (var point in controlPoints)
+ {
Pieces.RemoveAll(p => p.ControlPoint == point);
+ connections.RemoveAll(c => c.ControlPoint == point);
+ }
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 9b820261ab..2497e428fc 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override bool OnDoubleClick(DoubleClickEvent e)
{
// Todo: This should all not occur on double click, but rather if the previous control point is hovered.
- segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1];
+ segmentStart = HitObject.Path.ControlPoints[^1];
segmentStart.Type.Value = PathType.Linear;
currentSegmentLength = 1;
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
index 3437af8c1e..22b4c3e82e 100644
--- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
+++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit
///
private const double editor_hit_object_fade_out_extension = 500;
- public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods)
: base(ruleset, beatmap, mods)
{
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 812afaaa24..675b09fc6d 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
- protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 34e5a7f3cd..fe65ab78d1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (value != null)
{
- path.ControlPoints.AddRange(value.ControlPoints);
+ path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index fa69cec78d..2f43909332 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu
{
public class OsuRuleset : Ruleset
{
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuRuleset(this, beatmap, mods);
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableOsuRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
index bd59e8a03f..2686ba4fd2 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs
@@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Replays
// TODO: Shouldn't the spinner always spin in the same direction?
if (h is Spinner)
{
- calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection);
+ calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection);
- Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position;
+ Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position;
if (spinCentreOffset.Length > SPIN_RADIUS)
{
@@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Replays
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
{
- OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1];
+ OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[^1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
@@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Replays
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
- if (Frames[Frames.Count - 1].Time <= endFrame.Time)
+ if (Frames[^1].Time <= endFrame.Time)
AddFrameToReplay(endFrame);
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
index 02152fa51e..e96bd29ad5 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs
@@ -28,18 +28,18 @@ namespace osu.Game.Rulesets.Osu.Skinning
InternalChildren = new[]
{
+ ExpandTarget = new NonPlayfieldSprite
+ {
+ Texture = skin.GetTexture("cursor"),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
new NonPlayfieldSprite
{
Texture = skin.GetTexture("cursormiddle"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
- ExpandTarget = new NonPlayfieldSprite
- {
- Texture = skin.GetTexture("cursor"),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- }
};
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 80291c002e..4e86662ec6 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private void addPart(Vector2 screenSpacePosition)
{
parts[currentIndex].Position = screenSpacePosition;
- parts[currentIndex].Time = time;
+ parts[currentIndex].Time = time + 1;
++parts[currentIndex].InvalidationID;
currentIndex = (currentIndex + 1) % max_sprites;
@@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size;
- private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1);
+ private readonly QuadBatch vertexBatch = new QuadBatch(max_sprites, 1);
public TrailDrawNode(CursorTrail source)
: base(source)
@@ -227,23 +227,52 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
shader.Bind();
shader.GetUniform("g_FadeClock").UpdateValue(ref time);
- for (int i = 0; i < parts.Length; ++i)
+ texture.TextureGL.Bind();
+
+ RectangleF textureRect = texture.GetTextureRect();
+
+ foreach (var part in parts)
{
- if (parts[i].InvalidationID == -1)
+ if (part.InvalidationID == -1)
continue;
- vertexBatch.DrawTime = parts[i].Time;
+ if (time - part.Time >= 1)
+ continue;
- Vector2 pos = parts[i].Position;
+ vertexBatch.Add(new TexturedTrailVertex
+ {
+ Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2),
+ TexturePosition = textureRect.BottomLeft,
+ Colour = DrawColourInfo.Colour.BottomLeft.Linear,
+ Time = part.Time
+ });
- DrawQuad(
- texture,
- new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
- DrawColourInfo.Colour,
- null,
- vertexBatch.AddAction);
+ vertexBatch.Add(new TexturedTrailVertex
+ {
+ Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2),
+ TexturePosition = textureRect.BottomRight,
+ Colour = DrawColourInfo.Colour.BottomRight.Linear,
+ Time = part.Time
+ });
+
+ vertexBatch.Add(new TexturedTrailVertex
+ {
+ Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2),
+ TexturePosition = textureRect.TopRight,
+ Colour = DrawColourInfo.Colour.TopRight.Linear,
+ Time = part.Time
+ });
+
+ vertexBatch.Add(new TexturedTrailVertex
+ {
+ Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2),
+ TexturePosition = textureRect.TopLeft,
+ Colour = DrawColourInfo.Colour.TopLeft.Linear,
+ Time = part.Time
+ });
}
+ vertexBatch.Draw();
shader.Unbind();
}
@@ -253,25 +282,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
vertexBatch.Dispose();
}
-
- // Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
- private class TrailBatch : QuadBatch
- {
- public new readonly Action AddAction;
- public float DrawTime;
-
- public TrailBatch(int size, int maxBuffers)
- : base(size, maxBuffers)
- {
- AddAction = v => Add(new TexturedTrailVertex
- {
- Position = v.Position,
- TexturePosition = v.TexturePosition,
- Time = DrawTime + 1,
- Colour = v.Colour,
- });
- }
- }
}
[StructLayout(LayoutKind.Sequential)]
diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
index 5bb728a9b0..039d38e4fd 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
- public DrawableOsuRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
index 8522a42739..b2c8c7feda 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs
@@ -11,7 +11,6 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
@@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 768,
- Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty()) }
+ Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) }
});
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs
index d0db193738..140433a523 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Mods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
+ SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
return new ScoreAccessiblePlayer();
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index 6f4fbd0651..c41727557b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -3,7 +3,6 @@
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Scoring;
@@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
base.CreateNestedHitObjects();
if (IsStrong)
- AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime });
+ AddNested(new StrongHitObject { StartTime = this.GetEndTime() });
}
public override Judgement CreateJudgement() => new TaikoJudgement();
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
index e61953aeb8..4b234b56d4 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Replays;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Taiko.Beatmaps;
@@ -39,9 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
{
TaikoHitObject h = Beatmap.HitObjects[i];
-
- IHasEndTime endTimeData = h as IHasEndTime;
- double endTime = endTimeData?.EndTime ?? h.StartTime;
+ double endTime = h.GetEndTime();
switch (h)
{
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index b2655f592c..ab9c95159c 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko
{
public class TaikoRuleset : Ruleset
{
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods);
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableTaikoRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public const string SHORT_NAME = "taiko";
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index d4ea9a043a..2233658428 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false;
- public DrawableTaikoRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public DrawableTaikoRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Left;
diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
new file mode 100644
index 0000000000..589ec7e8aa
--- /dev/null
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
@@ -0,0 +1,427 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.States;
+using osu.Framework.Platform;
+using osu.Framework.Screens;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Scoring;
+using osu.Game.Screens;
+using osu.Game.Screens.Backgrounds;
+using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.PlayerSettings;
+using osu.Game.Screens.Select;
+using osu.Game.Tests.Resources;
+using osu.Game.Users;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Visual.Background
+{
+ [TestFixture]
+ public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ScreenWithBeatmapBackground),
+ typeof(PlayerLoader),
+ typeof(Player),
+ typeof(UserDimContainer),
+ typeof(OsuScreen)
+ };
+
+ private DummySongSelect songSelect;
+ private TestPlayerLoader playerLoader;
+ private TestPlayer player;
+ private BeatmapManager manager;
+ private RulesetStore rulesets;
+
+ [BackgroundDependencyLoader]
+ private void load(GameHost host, AudioManager audio)
+ {
+ Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
+ Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
+ Dependencies.Cache(new OsuConfigManager(LocalStorage));
+
+ manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
+
+ Beatmap.SetDefault();
+ }
+
+ [SetUp]
+ public virtual void SetUp() => Schedule(() =>
+ {
+ Child = new OsuScreenStack(songSelect = new DummySongSelect())
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+ });
+
+ ///
+ /// Check if properly triggers the visual settings preview when a user hovers over the visual settings panel.
+ ///
+ [Test]
+ public void PlayerLoaderSettingsHoverTest()
+ {
+ setupUserSettings();
+ AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true })));
+ AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
+ AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
+ AddStep("Trigger background preview", () =>
+ {
+ InputManager.MoveMouseTo(playerLoader.ScreenPos);
+ InputManager.MoveMouseTo(playerLoader.VisualSettingsPos);
+ });
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
+ waitForDim();
+ AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
+ }
+
+ ///
+ /// In the case of a user triggering the dim preview the instant player gets loaded, then moving the cursor off of the visual settings:
+ /// The OnHover of PlayerLoader will trigger, which could potentially cause visual settings to be unapplied unless checked for in PlayerLoader.
+ /// We need to check that in this scenario, the dim and blur is still properly applied after entering player.
+ ///
+ [Test]
+ public void PlayerLoaderTransitionTest()
+ {
+ performFullSetup();
+ AddStep("Trigger hover event", () => playerLoader.TriggerOnHover());
+ AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ }
+
+ ///
+ /// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard.
+ ///
+ [Test]
+ public void StoryboardBackgroundVisibilityTest()
+ {
+ performFullSetup();
+ createFakeStoryboard();
+ AddStep("Enable Storyboard", () =>
+ {
+ player.ReplacesBackground.Value = true;
+ player.StoryboardEnabled.Value = true;
+ });
+ waitForDim();
+ AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
+ AddStep("Disable Storyboard", () =>
+ {
+ player.ReplacesBackground.Value = false;
+ player.StoryboardEnabled.Value = false;
+ });
+ waitForDim();
+ AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
+ }
+
+ ///
+ /// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background.
+ ///
+ [Test]
+ public void StoryboardTransitionTest()
+ {
+ performFullSetup();
+ createFakeStoryboard();
+ AddStep("Exit to song select", () => player.Exit());
+ waitForDim();
+ AddAssert("Background is visible", () => songSelect.IsBackgroundVisible());
+ }
+
+ ///
+ /// Ensure is properly accepting user-defined visual changes for a background.
+ ///
+ [Test]
+ public void DisableUserDimBackgroundTest()
+ {
+ performFullSetup();
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
+ waitForDim();
+ AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
+ AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ }
+
+ ///
+ /// Ensure is properly accepting user-defined visual changes for a storyboard.
+ ///
+ [Test]
+ public void DisableUserDimStoryboardTest()
+ {
+ performFullSetup();
+ createFakeStoryboard();
+ AddStep("Enable Storyboard", () =>
+ {
+ player.ReplacesBackground.Value = true;
+ player.StoryboardEnabled.Value = true;
+ });
+ AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
+ AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
+ waitForDim();
+ AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible);
+ AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
+ waitForDim();
+ AddAssert("Storyboard is visible", () => player.IsStoryboardVisible);
+ }
+
+ ///
+ /// Check if the visual settings container retains dim and blur when pausing
+ ///
+ [Test]
+ public void PauseTest()
+ {
+ performFullSetup(true);
+ AddStep("Pause", () => player.Pause());
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ AddStep("Unpause", () => player.Resume());
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ }
+
+ ///
+ /// Check if the visual settings container removes user dim when suspending for
+ ///
+ [Test]
+ public void TransitionTest()
+ {
+ performFullSetup();
+ FadeAccessibleResults results = null;
+ AddStep("Transition to Results", () => player.Push(results =
+ new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
+ AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
+ waitForDim();
+ AddAssert("Screen is undimmed, original background retained", () =>
+ songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
+ }
+
+ ///
+ /// Check if background gets undimmed and unblurred when leaving for
+ ///
+ [Test]
+ public void TransitionOutTest()
+ {
+ performFullSetup();
+ AddStep("Exit to song select", () => player.Exit());
+ waitForDim();
+ AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
+ }
+
+ ///
+ /// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
+ ///
+ [Test]
+ public void ResumeFromPlayerTest()
+ {
+ performFullSetup();
+ AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos));
+ AddStep("Resume PlayerLoader", () => player.Restart());
+ waitForDim();
+ AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
+ waitForDim();
+ AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
+ }
+
+ private void waitForDim() => AddWaitStep("Wait for dim", 5);
+
+ private void createFakeStoryboard() => AddStep("Create storyboard", () =>
+ {
+ player.StoryboardEnabled.Value = false;
+ player.ReplacesBackground.Value = false;
+ player.DimmableStoryboard.Add(new OsuSpriteText
+ {
+ Size = new Vector2(500, 50),
+ Alpha = 1,
+ Colour = Color4.White,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = "THIS IS A STORYBOARD",
+ Font = new FontUsage(size: 50)
+ });
+ });
+
+ private void performFullSetup(bool allowPause = false)
+ {
+ setupUserSettings();
+
+ AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause))));
+
+ AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
+ AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
+ AddUntilStep("Wait for player to load", () => player.IsLoaded);
+ }
+
+ private void setupUserSettings()
+ {
+ AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
+ AddStep("Set default user settings", () =>
+ {
+ SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
+ songSelect.DimLevel.Value = 0.7f;
+ songSelect.BlurLevel.Value = 0.4f;
+ });
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ rulesets?.Dispose();
+ }
+
+ private class DummySongSelect : PlaySongSelect
+ {
+ protected override BackgroundScreen CreateBackground()
+ {
+ FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value);
+ DimEnabled.BindTo(background.EnableUserDim);
+ return background;
+ }
+
+ public readonly Bindable DimEnabled = new Bindable();
+ public readonly Bindable DimLevel = new Bindable();
+ public readonly Bindable BlurLevel = new Bindable();
+
+ public new BeatmapCarousel Carousel => base.Carousel;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ config.BindWith(OsuSetting.DimLevel, DimLevel);
+ config.BindWith(OsuSetting.BlurLevel, BlurLevel);
+ }
+
+ public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim);
+
+ public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
+
+ public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
+
+ public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
+
+ public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0;
+
+ public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1;
+
+ public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
+
+ ///
+ /// Make sure every time a screen gets pushed, the background doesn't get replaced
+ ///
+ /// Whether or not the original background (The one created in DummySongSelect) is still the current background
+ public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
+ }
+
+ private class FadeAccessibleResults : SoloResults
+ {
+ public FadeAccessibleResults(ScoreInfo score)
+ : base(score)
+ {
+ }
+
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+
+ public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
+ }
+
+ private class TestPlayer : Visual.TestPlayer
+ {
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+
+ public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
+
+ // Whether or not the player should be allowed to load.
+ public bool BlockLoad;
+
+ public Bindable StoryboardEnabled;
+ public readonly Bindable ReplacesBackground = new Bindable();
+ public readonly Bindable IsPaused = new Bindable();
+
+ public TestPlayer(bool allowPause = true)
+ : base(allowPause)
+ {
+ }
+
+ public bool IsStoryboardVisible => DimmableStoryboard.ContentDisplayed;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config, CancellationToken token)
+ {
+ while (BlockLoad && !token.IsCancellationRequested)
+ Thread.Sleep(1);
+
+ StoryboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard);
+ ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
+ DrawableRuleset.IsPaused.BindTo(IsPaused);
+ }
+ }
+
+ private class TestPlayerLoader : PlayerLoader
+ {
+ public VisualSettings VisualSettingsPos => VisualSettings;
+ public BackgroundScreen ScreenPos => Background;
+
+ public TestPlayerLoader(Player player)
+ : base(() => player)
+ {
+ }
+
+ public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
+
+ public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
+
+ protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
+ }
+
+ private class FadeAccessibleBackground : BackgroundScreenBeatmap
+ {
+ protected override DimmableBackground CreateFadeContainer() => dimmable = new TestDimmableBackground { RelativeSizeAxes = Axes.Both };
+
+ public Color4 CurrentColour => dimmable.CurrentColour;
+
+ public float CurrentAlpha => dimmable.CurrentAlpha;
+
+ public float CurrentDim => dimmable.DimLevel;
+
+ public Vector2 CurrentBlur => Background.BlurSigma;
+
+ private TestDimmableBackground dimmable;
+
+ public FadeAccessibleBackground(WorkingBeatmap beatmap)
+ : base(beatmap)
+ {
+ }
+ }
+
+ private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground
+ {
+ public Color4 CurrentColour => Content.Colour;
+ public float CurrentAlpha => Content.Alpha;
+
+ public new float DimLevel => base.DimLevel;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs
index 8f71584b4d..472c43096f 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs
@@ -1,423 +1,98 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input.Events;
-using osu.Framework.Input.States;
-using osu.Framework.Platform;
-using osu.Framework.Screens;
-using osu.Game.Beatmaps;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Osu.Mods;
-using osu.Game.Scoring;
-using osu.Game.Screens;
-using osu.Game.Screens.Backgrounds;
-using osu.Game.Screens.Play;
-using osu.Game.Screens.Play.PlayerSettings;
-using osu.Game.Screens.Select;
-using osu.Game.Tests.Resources;
-using osu.Game.Users;
-using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background
{
- [TestFixture]
- public class TestSceneUserDimContainer : ManualInputManagerTestScene
+ public class TestSceneUserDimContainer : OsuTestScene
{
- public override IReadOnlyList RequiredTypes => new[]
- {
- typeof(ScreenWithBeatmapBackground),
- typeof(PlayerLoader),
- typeof(Player),
- typeof(UserDimContainer),
- typeof(OsuScreen)
- };
+ private TestUserDimContainer userDimContainer;
- private DummySongSelect songSelect;
- private TestPlayerLoader playerLoader;
- private TestPlayer player;
- private BeatmapManager manager;
- private RulesetStore rulesets;
+ private readonly BindableBool isBreakTime = new BindableBool();
+
+ private Bindable lightenDuringBreaks = new Bindable();
[BackgroundDependencyLoader]
- private void load(GameHost host, AudioManager audio)
+ private void load(OsuConfigManager config)
{
- Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
- Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
- Dependencies.Cache(new OsuConfigManager(LocalStorage));
-
- manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
-
- Beatmap.SetDefault();
+ lightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks);
}
[SetUp]
- public virtual void SetUp() => Schedule(() =>
+ public void SetUp() => Schedule(() =>
{
- Child = new OsuScreenStack(songSelect = new DummySongSelect())
+ Child = userDimContainer = new TestUserDimContainer
{
- RelativeSizeAxes = Axes.Both
+ RelativeSizeAxes = Axes.Both,
+ Child = new Box
+ {
+ Colour = Color4.White,
+ RelativeSizeAxes = Axes.Both,
+ },
};
+
+ userDimContainer.IsBreakTime.BindTo(isBreakTime);
+ isBreakTime.Value = false;
+
+ lightenDuringBreaks.Value = false;
});
- ///
- /// Check if properly triggers the visual settings preview when a user hovers over the visual settings panel.
- ///
+ private const float test_user_dim = 0.6f;
+ private const float test_user_dim_lightened = test_user_dim - UserDimContainer.BREAK_LIGHTEN_AMOUNT;
+
+ [TestCase(test_user_dim, test_user_dim_lightened)]
+ [TestCase(0.2f, 0.0f)]
+ [TestCase(0.0f, 0.0f)]
+ public void TestBreakLightening(float userDim, float expectedBreakDim)
+ {
+ AddStep($"set dim level {userDim}", () => userDimContainer.UserDimLevel.Value = userDim);
+ AddStep("set lighten during break", () => lightenDuringBreaks.Value = true);
+
+ AddStep("set break", () => isBreakTime.Value = true);
+ AddUntilStep("has lightened", () => userDimContainer.DimEqual(expectedBreakDim));
+ AddStep("clear break", () => isBreakTime.Value = false);
+ AddUntilStep("not lightened", () => userDimContainer.DimEqual(userDim));
+ }
+
[Test]
- public void PlayerLoaderSettingsHoverTest()
+ public void TestEnableSettingDuringBreak()
{
- setupUserSettings();
- AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true })));
- AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
- AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
- AddStep("Trigger background preview", () =>
- {
- InputManager.MoveMouseTo(playerLoader.ScreenPos);
- InputManager.MoveMouseTo(playerLoader.VisualSettingsPos);
- });
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
- waitForDim();
- AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
+ AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim);
+
+ AddStep("set break", () => isBreakTime.Value = true);
+ AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim));
+ AddStep("set lighten during break", () => lightenDuringBreaks.Value = true);
+ AddUntilStep("has lightened", () => userDimContainer.DimEqual(test_user_dim_lightened));
}
- ///
- /// In the case of a user triggering the dim preview the instant player gets loaded, then moving the cursor off of the visual settings:
- /// The OnHover of PlayerLoader will trigger, which could potentially cause visual settings to be unapplied unless checked for in PlayerLoader.
- /// We need to check that in this scenario, the dim and blur is still properly applied after entering player.
- ///
[Test]
- public void PlayerLoaderTransitionTest()
+ public void TestDisableSettingDuringBreak()
{
- performFullSetup();
- AddStep("Trigger hover event", () => playerLoader.TriggerOnHover());
- AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
+ AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim);
+ AddStep("set lighten during break", () => lightenDuringBreaks.Value = true);
+
+ AddStep("set break", () => isBreakTime.Value = true);
+ AddUntilStep("has lightened", () => userDimContainer.DimEqual(test_user_dim_lightened));
+ AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false);
+ AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim));
+ AddStep("clear break", () => isBreakTime.Value = false);
+ AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim));
}
- ///
- /// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard.
- ///
- [Test]
- public void StoryboardBackgroundVisibilityTest()
+ private class TestUserDimContainer : UserDimContainer
{
- performFullSetup();
- createFakeStoryboard();
- AddStep("Enable Storyboard", () =>
- {
- player.ReplacesBackground.Value = true;
- player.StoryboardEnabled.Value = true;
- });
- waitForDim();
- AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
- AddStep("Disable Storyboard", () =>
- {
- player.ReplacesBackground.Value = false;
- player.StoryboardEnabled.Value = false;
- });
- waitForDim();
- AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
- }
+ public bool DimEqual(float expectedDimLevel) => Content.Colour == OsuColour.Gray(1f - expectedDimLevel);
- ///
- /// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background.
- ///
- [Test]
- public void StoryboardTransitionTest()
- {
- performFullSetup();
- createFakeStoryboard();
- AddStep("Exit to song select", () => player.Exit());
- waitForDim();
- AddAssert("Background is visible", () => songSelect.IsBackgroundVisible());
- }
-
- ///
- /// Ensure is properly accepting user-defined visual changes for a background.
- ///
- [Test]
- public void DisableUserDimBackgroundTest()
- {
- performFullSetup();
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
- waitForDim();
- AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
- AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- }
-
- ///
- /// Ensure is properly accepting user-defined visual changes for a storyboard.
- ///
- [Test]
- public void DisableUserDimStoryboardTest()
- {
- performFullSetup();
- createFakeStoryboard();
- AddStep("Enable Storyboard", () =>
- {
- player.ReplacesBackground.Value = true;
- player.StoryboardEnabled.Value = true;
- });
- AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
- AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
- waitForDim();
- AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible);
- AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
- waitForDim();
- AddAssert("Storyboard is visible", () => player.IsStoryboardVisible);
- }
-
- ///
- /// Check if the visual settings container retains dim and blur when pausing
- ///
- [Test]
- public void PauseTest()
- {
- performFullSetup(true);
- AddStep("Pause", () => player.Pause());
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- AddStep("Unpause", () => player.Resume());
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- }
-
- ///
- /// Check if the visual settings container removes user dim when suspending for
- ///
- [Test]
- public void TransitionTest()
- {
- performFullSetup();
- FadeAccessibleResults results = null;
- AddStep("Transition to Results", () => player.Push(results =
- new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
- AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
- waitForDim();
- AddAssert("Screen is undimmed, original background retained", () =>
- songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
- }
-
- ///
- /// Check if background gets undimmed and unblurred when leaving for
- ///
- [Test]
- public void TransitionOutTest()
- {
- performFullSetup();
- AddStep("Exit to song select", () => player.Exit());
- waitForDim();
- AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
- }
-
- ///
- /// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
- ///
- [Test]
- public void ResumeFromPlayerTest()
- {
- performFullSetup();
- AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos));
- AddStep("Resume PlayerLoader", () => player.Restart());
- waitForDim();
- AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
- AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
- waitForDim();
- AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
- }
-
- private void waitForDim() => AddWaitStep("Wait for dim", 5);
-
- private void createFakeStoryboard() => AddStep("Create storyboard", () =>
- {
- player.StoryboardEnabled.Value = false;
- player.ReplacesBackground.Value = false;
- player.DimmableStoryboard.Add(new OsuSpriteText
- {
- Size = new Vector2(500, 50),
- Alpha = 1,
- Colour = Color4.White,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Text = "THIS IS A STORYBOARD",
- Font = new FontUsage(size: 50)
- });
- });
-
- private void performFullSetup(bool allowPause = false)
- {
- setupUserSettings();
-
- AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause))));
-
- AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
- AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
- AddUntilStep("Wait for player to load", () => player.IsLoaded);
- }
-
- private void setupUserSettings()
- {
- AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
- AddStep("Set default user settings", () =>
- {
- Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
- songSelect.DimLevel.Value = 0.7f;
- songSelect.BlurLevel.Value = 0.4f;
- });
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
- rulesets?.Dispose();
- }
-
- private class DummySongSelect : PlaySongSelect
- {
- protected override BackgroundScreen CreateBackground()
- {
- FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value);
- DimEnabled.BindTo(background.EnableUserDim);
- return background;
- }
-
- public readonly Bindable DimEnabled = new Bindable();
- public readonly Bindable DimLevel = new Bindable();
- public readonly Bindable BlurLevel = new Bindable();
-
- public new BeatmapCarousel Carousel => base.Carousel;
-
- [BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
- {
- config.BindWith(OsuSetting.DimLevel, DimLevel);
- config.BindWith(OsuSetting.BlurLevel, BlurLevel);
- }
-
- public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1 - (float)DimLevel.Value);
-
- public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
-
- public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
-
- public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
-
- public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0;
-
- public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1;
-
- public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
-
- ///
- /// Make sure every time a screen gets pushed, the background doesn't get replaced
- ///
- /// Whether or not the original background (The one created in DummySongSelect) is still the current background
- public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
- }
-
- private class FadeAccessibleResults : SoloResults
- {
- public FadeAccessibleResults(ScoreInfo score)
- : base(score)
- {
- }
-
- protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
-
- public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
- }
-
- private class TestPlayer : Visual.TestPlayer
- {
- protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
-
- public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
-
- // Whether or not the player should be allowed to load.
- public bool BlockLoad;
-
- public Bindable StoryboardEnabled;
- public readonly Bindable ReplacesBackground = new Bindable();
- public readonly Bindable IsPaused = new Bindable();
-
- public TestPlayer(bool allowPause = true)
- : base(allowPause)
- {
- }
-
- public bool IsStoryboardVisible => DimmableStoryboard.ContentDisplayed;
-
- [BackgroundDependencyLoader]
- private void load(OsuConfigManager config, CancellationToken token)
- {
- while (BlockLoad && !token.IsCancellationRequested)
- Thread.Sleep(1);
-
- StoryboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard);
- ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
- DrawableRuleset.IsPaused.BindTo(IsPaused);
- }
- }
-
- private class TestPlayerLoader : PlayerLoader
- {
- public VisualSettings VisualSettingsPos => VisualSettings;
- public BackgroundScreen ScreenPos => Background;
-
- public TestPlayerLoader(Player player)
- : base(() => player)
- {
- }
-
- public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
-
- public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
-
- protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
- }
-
- private class FadeAccessibleBackground : BackgroundScreenBeatmap
- {
- protected override DimmableBackground CreateFadeContainer() => dimmable = new TestDimmableBackground { RelativeSizeAxes = Axes.Both };
-
- public Color4 CurrentColour => dimmable.CurrentColour;
-
- public float CurrentAlpha => dimmable.CurrentAlpha;
-
- public Vector2 CurrentBlur => Background.BlurSigma;
-
- private TestDimmableBackground dimmable;
-
- public FadeAccessibleBackground(WorkingBeatmap beatmap)
- : base(beatmap)
- {
- }
- }
-
- private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground
- {
- public Color4 CurrentColour => Content.Colour;
- public float CurrentAlpha => Content.Alpha;
+ public new Bindable UserDimLevel => base.UserDimLevel;
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
index 5ee109e3dd..069b965d9b 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
+ SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer();
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
index 684e79b3f5..c958932730 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs
@@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
var ruleset = new TestScrollingRuleset();
- drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap), Array.Empty());
+ drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap).GetPlayableBeatmap(ruleset.RulesetInfo));
drawableRuleset.FrameStablePlayback = false;
overrideAction?.Invoke(drawableRuleset);
@@ -201,7 +201,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public override IEnumerable GetModsFor(ModType type) => throw new NotImplementedException();
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new TestDrawableScrollingRuleset(this, beatmap, mods);
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap);
@@ -222,7 +222,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public new Bindable TimeRange => base.TimeRange;
- public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public TestDrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
TimeRange.Value = time_range;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
index f4e8a68819..992c47f856 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Array.Empty();
+ SelectedMods.Value = Array.Empty();
return new FailPlayer();
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
index cca6301b02..1580aac8c5 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Array.Empty();
+ SelectedMods.Value = Array.Empty();
return new FailPlayer();
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
index b2b58a63fb..5336c720a1 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
@@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Player CreatePlayer(Ruleset ruleset)
{
- Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
+ SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new RulesetExposingPlayer();
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
new file mode 100644
index 0000000000..39c42980ab
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -0,0 +1,81 @@
+// 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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneHUDOverlay : ManualInputManagerTestScene
+ {
+ private HUDOverlay hudOverlay;
+
+ private Drawable hideTarget => hudOverlay.KeyCounter; // best way of checking hideTargets without exposing.
+
+ [Resolved]
+ private OsuConfigManager config { get; set; }
+
+ [Test]
+ public void TestShownByDefault()
+ {
+ createNew();
+
+ AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
+
+ AddAssert("hidetarget is visible", () => hideTarget.IsPresent);
+ AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
+ }
+
+ [Test]
+ public void TestFadesInOnLoadComplete()
+ {
+ float? initialAlpha = null;
+
+ createNew(h => h.OnLoadComplete += _ => initialAlpha = hideTarget.Alpha);
+ AddUntilStep("wait for load", () => hudOverlay.IsAlive);
+ AddAssert("initial alpha was less than 1", () => initialAlpha != null && initialAlpha < 1);
+ }
+
+ [Test]
+ public void TestHideExternally()
+ {
+ createNew();
+
+ AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
+
+ AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
+ AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
+ }
+
+ [Test]
+ public void TestExternalHideDoesntAffectConfig()
+ {
+ bool originalConfigValue = false;
+
+ createNew();
+
+ AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.ShowInterface));
+
+ AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
+ AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.ShowInterface));
+
+ AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
+ AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.ShowInterface));
+ }
+
+ private void createNew(Action action = null)
+ {
+ AddStep("create overlay", () =>
+ {
+ Child = hudOverlay = new HUDOverlay(null, null, Array.Empty());
+
+ action?.Invoke(hudOverlay);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
index f02361e685..f68f4b8b83 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay
beforeLoadAction?.Invoke();
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToTrack(Beatmap.Value.Track);
InputManager.Child = container = new TestPlayerLoaderContainer(
@@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestEarlyExit()
{
- AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() }));
+ AddStep("load dummy beatmap", () => ResetPlayer(false, () => SelectedMods.Value = new[] { new OsuModNightcore() }));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1);
AddStep("exit loader", () => loader.Exit());
@@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay
TestMod playerMod1 = null;
TestMod playerMod2 = null;
- AddStep("load player", () => { ResetPlayer(true, () => Mods.Value = new[] { gameMod = new TestMod() }); });
+ AddStep("load player", () => { ResetPlayer(true, () => SelectedMods.Value = new[] { gameMod = new TestMod() }); });
AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs
index 2a45e68c0a..96c0c59695 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("set second set", () => details.BeatmapSet = secondSet);
AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics);
- BeatmapSetInfo createSet() => new BeatmapSetInfo
+ static BeatmapSetInfo createSet() => new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() },
Beatmaps = new List
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 5dd02c1ddd..00fa95bedc 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -256,17 +256,17 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("change ruleset", () =>
{
- Mods.ValueChanged += onModChange;
+ SelectedMods.ValueChanged += onModChange;
songSelect.Ruleset.ValueChanged += onRulesetChange;
Ruleset.Value = new TaikoRuleset().RulesetInfo;
- Mods.ValueChanged -= onModChange;
+ SelectedMods.ValueChanged -= onModChange;
songSelect.Ruleset.ValueChanged -= onRulesetChange;
});
AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
- AddAssert("empty mods", () => !Mods.Value.Any());
+ AddAssert("empty mods", () => !SelectedMods.Value.Any());
void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++;
void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex++;
@@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestModsRetainedBetweenSongSelect()
{
- AddAssert("empty mods", () => !Mods.Value.Any());
+ AddAssert("empty mods", () => !SelectedMods.Value.Any());
createSongSelect();
@@ -285,7 +285,7 @@ namespace osu.Game.Tests.Visual.SongSelect
createSongSelect();
- AddAssert("mods retained", () => Mods.Value.Any());
+ AddAssert("mods retained", () => SelectedMods.Value.Any());
}
[Test]
@@ -332,7 +332,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void checkMusicPlaying(bool playing) =>
AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing);
- private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods);
+ private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
index ed44d82bce..b0b673d6a4 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs
@@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
{
- if (timingPoints[timingPoints.Count - 1] == current)
+ if (timingPoints[^1] == current)
return current;
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
@@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
if (timingPoints.Count == 0) return 0;
- if (timingPoints[timingPoints.Count - 1] == current)
+ if (timingPoints[^1] == current)
return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength);
return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
index be50200e1c..12ee4ceb2e 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs
@@ -8,13 +8,16 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Mods.Sections;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.HUD;
@@ -48,42 +51,48 @@ namespace osu.Game.Tests.Visual.UserInterface
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
+ }
- Add(modSelect = new TestModSelectOverlay
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
- });
+ modSelect = new TestModSelectOverlay
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ },
- Add(modDisplay = new ModDisplay
- {
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- AutoSizeAxes = Axes.Both,
- Position = new Vector2(0, 25),
- });
+ modDisplay = new ModDisplay
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ AutoSizeAxes = Axes.Both,
+ Position = new Vector2(0, 25),
+ Current = { BindTarget = modSelect.SelectedMods }
+ }
+ };
+ });
- modDisplay.Current.UnbindBindings();
- modDisplay.Current.BindTo(modSelect.SelectedMods);
-
- AddStep("Show", modSelect.Show);
- AddStep("Toggle", modSelect.ToggleVisibility);
- AddStep("Toggle", modSelect.ToggleVisibility);
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("show", () => modSelect.Show());
}
[Test]
public void TestOsuMods()
{
- var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0);
- changeRuleset(ruleset);
+ changeRuleset(0);
- var instance = ruleset.CreateInstance();
+ var osu = new OsuRuleset();
- var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
- var harderMods = instance.GetModsFor(ModType.DifficultyIncrease);
+ var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
+ var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
- var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
+ var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
@@ -97,8 +106,8 @@ namespace osu.Game.Tests.Visual.UserInterface
testMultiMod(doubleTimeMod);
testIncompatibleMods(easy, hardRock);
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
- testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
- testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
+ testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour);
+ testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour);
testUnimplementedMod(spunOutMod);
}
@@ -106,37 +115,31 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestManiaMods()
{
- var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3);
- changeRuleset(ruleset);
+ changeRuleset(3);
- testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
+ testRankedText(new ManiaRuleset().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
}
[Test]
public void TestRulesetChanges()
{
- var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0);
- var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3);
+ changeRuleset(0);
- changeRuleset(null);
+ var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
- var instance = rulesetOsu.CreateInstance();
- var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
- var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
+ AddStep("set mods externally", () => { SelectedMods.Value = new[] { noFailMod }; });
- AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; });
-
- changeRuleset(rulesetOsu);
+ changeRuleset(0);
AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
- changeRuleset(rulesetMania);
+ changeRuleset(3);
- AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail));
+ AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0);
- changeRuleset(rulesetOsu);
+ changeRuleset(0);
- AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any());
+ AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0);
}
private void testSingleMod(Mod mod)
@@ -198,19 +201,19 @@ namespace osu.Game.Tests.Visual.UserInterface
selectNext(mod);
AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any());
- AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke);
+ AddStep("deselect all", () => modSelect.DeselectAllButton.Action.Invoke());
AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any());
}
- private void testMultiplierTextColour(Mod mod, Color4 colour)
+ private void testMultiplierTextColour(Mod mod, Func getCorrectColour)
{
- checkLabelColor(Color4.White);
+ checkLabelColor(() => Color4.White);
selectNext(mod);
AddWaitStep("wait for changing colour", 1);
- checkLabelColor(colour);
+ checkLabelColor(getCorrectColour);
selectPrevious(mod);
AddWaitStep("wait for changing colour", 1);
- checkLabelColor(Color4.White);
+ checkLabelColor(() => Color4.White);
}
private void testRankedText(Mod mod)
@@ -235,9 +238,9 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
- private void changeRuleset(RulesetInfo ruleset)
+ private void changeRuleset(int? id)
{
- AddStep($"change ruleset to {ruleset}", () => { Ruleset.Value = ruleset; });
+ AddStep($"change ruleset to {(id?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); });
waitForLoad();
}
@@ -253,7 +256,7 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
- private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color);
+ private void checkLabelColor(Func getColour) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == getColour());
private class TestModSelectOverlay : ModSelectOverlay
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
index fc44c5f595..8dcb7dcbf8 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs
@@ -2,15 +2,20 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Allocation;
+using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Tests.Visual.UserInterface
{
@@ -18,28 +23,51 @@ namespace osu.Game.Tests.Visual.UserInterface
{
private TestModSelectOverlay modSelect;
- [BackgroundDependencyLoader]
- private void load()
+ private readonly Mod testCustomisableMod = new TestModCustomisable1();
+
+ [Test]
+ public void TestButtonShowsOnCustomisableMod()
{
- Add(modSelect = new TestModSelectOverlay
- {
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.BottomCentre,
- Anchor = Anchor.BottomCentre,
- });
+ createModSelect();
- var testMod = new TestModCustomisable1();
-
- AddStep("open", modSelect.Show);
+ AddStep("open", () => modSelect.Show());
AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value);
AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded);
- AddStep("select mod", () => modSelect.SelectMod(testMod));
+ AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod));
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
AddStep("open Customisation", () => modSelect.CustomiseButton.Click());
- AddStep("deselect mod", () => modSelect.SelectMod(testMod));
+ AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod));
AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0);
}
+ [Test]
+ public void TestButtonShowsOnModAlreadyAdded()
+ {
+ AddStep("set active mods", () => SelectedMods.Value = new List { testCustomisableMod });
+
+ createModSelect();
+
+ AddAssert("mods still active", () => SelectedMods.Value.Count == 1);
+
+ AddStep("open", () => modSelect.Show());
+ AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
+ }
+
+ private void createModSelect()
+ {
+ AddStep("create mod select", () =>
+ {
+ Ruleset.Value = new TestRulesetInfo();
+
+ Child = modSelect = new TestModSelectOverlay
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ };
+ });
+ }
+
private class TestModSelectOverlay : ModSelectOverlay
{
public new Container ModSettingsContainer => base.ModSettingsContainer;
@@ -50,24 +78,41 @@ namespace osu.Game.Tests.Visual.UserInterface
public void SelectMod(Mod mod) =>
ModSectionsContainer.Children.Single(s => s.ModType == mod.Type)
.ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1);
+ }
- protected override void LoadComplete()
+ public class TestRulesetInfo : RulesetInfo
+ {
+ public override Ruleset CreateInstance() => new TestCustomisableModRuleset();
+
+ public TestRulesetInfo()
{
- base.LoadComplete();
+ Available = true;
+ }
- foreach (var section in ModSectionsContainer)
+ public class TestCustomisableModRuleset : Ruleset
+ {
+ public override IEnumerable GetModsFor(ModType type)
{
- if (section.ModType == ModType.Conversion)
+ if (type == ModType.Conversion)
{
- section.Mods = new Mod[]
+ return new Mod[]
{
new TestModCustomisable1(),
new TestModCustomisable2()
};
}
- else
- section.Mods = Array.Empty();
+
+ return Array.Empty();
}
+
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException();
+
+ public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
+
+ public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
+
+ public override string Description { get; } = "test";
+ public override string ShortName { get; } = "tst";
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index ce2783004c..03496952e7 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -195,8 +195,8 @@ namespace osu.Game.Beatmaps.ControlPoints
if (time < list[0].Time)
return null;
- if (time >= list[list.Count - 1].Time)
- return list[list.Count - 1];
+ if (time >= list[^1].Time)
+ return list[^1];
int l = 0;
int r = list.Count - 2;
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index a3ab01c886..59a27e3fde 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -57,7 +57,7 @@ namespace osu.Game.Beatmaps
{
public override IEnumerable GetModsFor(ModType type) => new Mod[] { };
- public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods)
+ public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null)
{
throw new NotImplementedException();
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 168b912af6..e194280607 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps.Formats
if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
{
- if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
+ if (!Enum.TryParse(line[1..^1], out section))
{
Logger.Log($"Unknown section \"{line}\" in \"{output}\"");
section = Section.None;
diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs
index a087a52ada..5f1f0d1e40 100644
--- a/osu.Game/Beatmaps/IWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs
@@ -62,6 +62,6 @@ namespace osu.Game.Beatmaps
/// The s to apply to the .
/// The converted .
/// If could not be converted to .
- IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods);
+ IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null);
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 4452d26fcd..1255665cf0 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -99,8 +99,10 @@ namespace osu.Game.Beatmaps
/// The applicable .
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
- public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods)
+ public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null)
{
+ mods ??= Array.Empty();
+
var rulesetInstance = ruleset.CreateInstance();
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index b71463841a..947e864a87 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -80,6 +80,7 @@ namespace osu.Game.Configuration
// Gameplay
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
+ Set(OsuSetting.LightenDuringBreaks, true);
Set(OsuSetting.HitLighting, true);
@@ -142,6 +143,7 @@ namespace osu.Game.Configuration
AutoCursorSize,
DimLevel,
BlurLevel,
+ LightenDuringBreaks,
ShowStoryboard,
ShowVideoBackground,
KeyOverlay,
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index 2bbac92f7f..9735f6373d 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Graphics.Containers
foreach (var link in links)
{
- AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd));
+ AddText(text[previousLinkEnd..link.Index]);
AddLink(text.Substring(link.Index, link.Length), link.Action, link.Argument ?? link.Url);
previousLinkEnd = link.Index + link.Length;
}
diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
index 7683bbcd63..e67cd94d5c 100644
--- a/osu.Game/Graphics/Containers/UserDimContainer.cs
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -1,11 +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.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
+using osu.Game.Screens.Play;
namespace osu.Game.Graphics.Containers
{
@@ -14,7 +16,12 @@ namespace osu.Game.Graphics.Containers
///
public abstract class UserDimContainer : Container
{
- protected const float BACKGROUND_FADE_DURATION = 800;
+ ///
+ /// Amount of lightening to apply to current dim level during break times.
+ ///
+ public const float BREAK_LIGHTEN_AMOUNT = 0.3f;
+
+ protected const double BACKGROUND_FADE_DURATION = 800;
///
/// Whether or not user-configured dim levels should be applied to the container.
@@ -26,6 +33,12 @@ namespace osu.Game.Graphics.Containers
///
public readonly Bindable StoryboardReplacesBackground = new Bindable();
+ ///
+ /// Whether player is in break time.
+ /// Must be bound to to allow for dim adjustments in gameplay.
+ ///
+ public readonly IBindable IsBreakTime = new Bindable();
+
///
/// Whether the content of this container is currently being displayed.
///
@@ -33,11 +46,14 @@ namespace osu.Game.Graphics.Containers
protected Bindable UserDimLevel { get; private set; }
+ protected Bindable LightenDuringBreaks { get; private set; }
+
protected Bindable ShowStoryboard { get; private set; }
protected Bindable ShowVideo { get; private set; }
- protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0;
+ private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
+ protected float DimLevel => Math.Max(EnableUserDim.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
protected override Container Content => dimContent;
@@ -55,11 +71,14 @@ namespace osu.Game.Graphics.Containers
private void load(OsuConfigManager config)
{
UserDimLevel = config.GetBindable(OsuSetting.DimLevel);
+ LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks);
ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground);
EnableUserDim.ValueChanged += _ => UpdateVisuals();
UserDimLevel.ValueChanged += _ => UpdateVisuals();
+ LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
+ IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
@@ -84,7 +103,7 @@ namespace osu.Game.Graphics.Containers
ContentDisplayed = ShowDimContent;
dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
- dimContent.FadeColour(OsuColour.Gray(1 - (float)DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint);
+ dimContent.FadeColour(OsuColour.Gray(1f - DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index f310da3883..22b8d9d012 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -80,8 +80,13 @@ namespace osu.Game
// todo: move this to SongSelect once Screen has the ability to unsuspend.
[Cached]
- [Cached(Type = typeof(IBindable>))]
- protected readonly Bindable> Mods = new Bindable>(Array.Empty());
+ [Cached(typeof(IBindable>))]
+ protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
+
+ ///
+ /// Mods available for the current .
+ ///
+ public readonly Bindable>> AvailableMods = new Bindable>>();
protected Bindable Beatmap { get; private set; } // cached via load() method
@@ -233,6 +238,23 @@ namespace osu.Game
PreviewTrackManager previewTrackManager;
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
Add(previewTrackManager);
+
+ Ruleset.BindValueChanged(onRulesetChanged);
+ }
+
+ private void onRulesetChanged(ValueChangedEvent r)
+ {
+ var dict = new Dictionary>();
+
+ if (r.NewValue?.Available == true)
+ {
+ foreach (ModType type in Enum.GetValues(typeof(ModType)))
+ dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList();
+ }
+
+ if (!SelectedMods.Disabled)
+ SelectedMods.Value = Array.Empty();
+ AvailableMods.Value = dict;
}
protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer();
diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs
index f4b588ddb3..10b3bc7c2b 100644
--- a/osu.Game/Overlays/Mods/ModControlSection.cs
+++ b/osu.Game/Overlays/Mods/ModControlSection.cs
@@ -1,10 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
@@ -12,14 +12,13 @@ using osuTK;
namespace osu.Game.Overlays.Mods
{
- public class ModControlSection : Container
+ public class ModControlSection : CompositeDrawable
{
protected FillFlowContainer FlowContent;
- protected override Container Content => FlowContent;
public readonly Mod Mod;
- public ModControlSection(Mod mod)
+ public ModControlSection(Mod mod, IEnumerable modControls)
{
Mod = mod;
@@ -33,9 +32,8 @@ namespace osu.Game.Overlays.Mods
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
+ ChildrenEnumerable = modControls
};
-
- AddRange(Mod.CreateSettingsControls());
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index e8ea43e3f2..7f07ce620c 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -20,7 +20,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods.Sections;
-using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osuTK;
@@ -50,7 +49,7 @@ namespace osu.Game.Overlays.Mods
protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
- protected readonly IBindable Ruleset = new Bindable();
+ private Bindable>> availableMods;
protected Color4 LowMultiplierColour;
protected Color4 HighMultiplierColour;
@@ -322,14 +321,14 @@ namespace osu.Game.Overlays.Mods
}
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods)
+ private void load(OsuColour colours, AudioManager audio, Bindable> selectedMods, OsuGameBase osu)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
- Ruleset.BindTo(ruleset);
- if (mods != null) SelectedMods.BindTo(mods);
+ availableMods = osu.AvailableMods.GetBoundCopy();
+ SelectedMods.BindTo(selectedMods);
sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off");
@@ -360,7 +359,7 @@ namespace osu.Game.Overlays.Mods
{
base.LoadComplete();
- Ruleset.BindValueChanged(rulesetChanged, true);
+ availableMods.BindValueChanged(availableModsChanged, true);
SelectedMods.BindValueChanged(selectedModsChanged, true);
}
@@ -410,22 +409,12 @@ namespace osu.Game.Overlays.Mods
return base.OnKeyDown(e);
}
- private void rulesetChanged(ValueChangedEvent e)
+ private void availableModsChanged(ValueChangedEvent>> mods)
{
- if (e.NewValue == null) return;
-
- var instance = e.NewValue.CreateInstance();
+ if (mods.NewValue == null) return;
foreach (var section in ModSectionsContainer.Children)
- section.Mods = instance.GetModsFor(section.ModType);
-
- // attempt to re-select any already selected mods.
- // this may be the first time we are receiving the ruleset, in which case they will still match.
- selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value));
-
- // write the mods back to the SelectedMods bindable in the case a change was not applicable.
- // this generally isn't required as the previous line will perform deselection; just here for safety.
- refreshSelectedMods();
+ section.Mods = mods.NewValue[section.ModType];
}
private void selectedModsChanged(ValueChangedEvent> mods)
@@ -462,17 +451,17 @@ namespace osu.Game.Overlays.Mods
private void updateModSettings(ValueChangedEvent> selectedMods)
{
- foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue))
+ ModSettingsContent.Clear();
+
+ foreach (var mod in selectedMods.NewValue)
{
- var controls = added.CreateSettingsControls().ToList();
- if (controls.Count > 0)
- ModSettingsContent.Add(new ModControlSection(added) { Children = controls });
+ var settings = mod.CreateSettingsControls().ToList();
+ if (settings.Count > 0)
+ ModSettingsContent.Add(new ModControlSection(mod, settings));
}
- foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue))
- ModSettingsContent.RemoveAll(section => section.Mod == removed);
+ bool hasSettings = ModSettingsContent.Count > 0;
- bool hasSettings = ModSettingsContent.Children.Count > 0;
CustomiseButton.Enabled.Value = hasSettings;
if (!hasSettings)
@@ -502,8 +491,8 @@ namespace osu.Game.Overlays.Mods
{
base.Dispose(isDisposing);
- Ruleset.UnbindAll();
- SelectedMods.UnbindAll();
+ availableMods?.UnbindAll();
+ SelectedMods?.UnbindAll();
}
#endregion
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
index f4aa9a0144..3f8bc2b0c7 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
@@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
KeyboardStep = 0.01f
},
new SettingsCheckbox
+ {
+ LabelText = "Lighten playfield during breaks",
+ Bindable = config.GetBindable(OsuSetting.LightenDuringBreaks)
+ },
+ new SettingsCheckbox
{
LabelText = "Show score overlay",
Bindable = config.GetBindable(OsuSetting.ShowInterface)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 9ac967ef74..22d94abcb9 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Edit
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
- private IWorkingBeatmap workingBeatmap;
private Beatmap playableBeatmap;
private IBeatmapProcessor beatmapProcessor;
@@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Edit
{
try
{
- drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap, Array.Empty()))
+ drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, playableBeatmap))
{
Clock = framedClock,
ProcessCustomClock = false
@@ -145,8 +144,7 @@ namespace osu.Game.Rulesets.Edit
{
var parentWorkingBeatmap = parent.Get>().Value;
- playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty());
- workingBeatmap = new EditorWorkingBeatmap(playableBeatmap, parentWorkingBeatmap);
+ playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo);
beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap);
@@ -250,7 +248,7 @@ namespace osu.Game.Rulesets.Edit
protected abstract IReadOnlyList CompositionTools { get; }
- protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods);
+ protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null);
public void BeginPlacement(HitObject hitObject)
{
diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs
index dcb3cb5597..71a666414f 100644
--- a/osu.Game/Rulesets/Mods/ModDaycore.cs
+++ b/osu.Game/Rulesets/Mods/ModDaycore.cs
@@ -1,7 +1,9 @@
// 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.Bindables;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Mods
@@ -13,9 +15,23 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.Question;
public override string Description => "Whoaaaaa...";
+ private readonly BindableNumber tempoAdjust = new BindableDouble(1);
+ private readonly BindableNumber freqAdjust = new BindableDouble(1);
+
+ protected ModDaycore()
+ {
+ SpeedChange.BindValueChanged(val =>
+ {
+ freqAdjust.Value = SpeedChange.Default;
+ tempoAdjust.Value = val.NewValue / SpeedChange.Default;
+ }, true);
+ }
+
public override void ApplyToTrack(Track track)
{
- track.Frequency.Value *= RateAdjust;
+ // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
+ track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
+ track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
index 5e685b040e..7015460c51 100644
--- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs
+++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
@@ -3,12 +3,14 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModDoubleTime : ModTimeAdjust
+ public abstract class ModDoubleTime : ModRateAdjust
{
public override string Name => "Double Time";
public override string Acronym => "DT";
@@ -19,6 +21,14 @@ namespace osu.Game.Rulesets.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray();
- protected override double RateAdjust => 1.5;
+ [SettingSource("Speed increase", "The actual increase to apply")]
+ public override BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ MinValue = 1.01,
+ MaxValue = 2,
+ Default = 1.5,
+ Value = 1.5,
+ Precision = 0.01,
+ };
}
}
diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs
index d17ddd2253..15f7afa312 100644
--- a/osu.Game/Rulesets/Mods/ModHalfTime.cs
+++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs
@@ -3,12 +3,14 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHalfTime : ModTimeAdjust
+ public abstract class ModHalfTime : ModRateAdjust
{
public override string Name => "Half Time";
public override string Acronym => "HT";
@@ -19,6 +21,14 @@ namespace osu.Game.Rulesets.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray();
- protected override double RateAdjust => 0.75;
+ [SettingSource("Speed decrease", "The actual decrease to apply")]
+ public override BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ MinValue = 0.5,
+ MaxValue = 0.99,
+ Default = 0.75,
+ Value = 0.75,
+ Precision = 0.01,
+ };
}
}
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index a4f1ef5a72..c14e02e64d 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -1,7 +1,9 @@
// 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.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
@@ -14,9 +16,23 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => OsuIcon.ModNightcore;
public override string Description => "Uguuuuuuuu...";
+ private readonly BindableNumber tempoAdjust = new BindableDouble(1);
+ private readonly BindableNumber freqAdjust = new BindableDouble(1);
+
+ protected ModNightcore()
+ {
+ SpeedChange.BindValueChanged(val =>
+ {
+ freqAdjust.Value = SpeedChange.Default;
+ tempoAdjust.Value = val.NewValue / SpeedChange.Default;
+ }, true);
+ }
+
public override void ApplyToTrack(Track track)
{
- track.Frequency.Value *= RateAdjust;
+ // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
+ track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
+ track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs
similarity index 54%
rename from osu.Game/Rulesets/Mods/ModTimeAdjust.cs
rename to osu.Game/Rulesets/Mods/ModRateAdjust.cs
index 7d0cc2a7c3..1739524bcd 100644
--- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs
+++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs
@@ -1,20 +1,19 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeAdjust : Mod, IApplicableToTrack
+ public abstract class ModRateAdjust : Mod, IApplicableToTrack
{
- public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
-
- protected abstract double RateAdjust { get; }
+ public abstract BindableNumber SpeedChange { get; }
public virtual void ApplyToTrack(Track track)
{
- track.Tempo.Value *= RateAdjust;
+ track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
index 839b2ae36e..133f9ceb39 100644
--- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs
+++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
@@ -3,42 +3,58 @@
using System;
using System.Linq;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap
+ public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToBeatmap, IApplicableToTrack
{
///
/// The point in the beatmap at which the final ramping rate should be reached.
///
private const double final_rate_progress = 0.75f;
- public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) };
-
- protected abstract double FinalRateAdjustment { get; }
+ [SettingSource("Final rate", "The final speed to ramp to")]
+ public abstract BindableNumber FinalRate { get; }
private double finalRateTime;
private double beginRampTime;
+
+ public BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ Default = 1,
+ Value = 1,
+ Precision = 0.01,
+ };
+
private Track track;
- public virtual void ApplyToTrack(Track track)
+ protected ModTimeRamp()
+ {
+ // for preview purpose at song select. eventually we'll want to be able to update every frame.
+ FinalRate.BindValueChanged(val => applyAdjustment(1), true);
+ }
+
+ public void ApplyToTrack(Track track)
{
this.track = track;
+ track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
- lastAdjust = 1;
-
- // for preview purposes. during gameplay, Update will overwrite this setting.
- applyAdjustment(1);
+ FinalRate.TriggerChange();
}
public virtual void ApplyToBeatmap(IBeatmap beatmap)
{
HitObject lastObject = beatmap.HitObjects.LastOrDefault();
+ SpeedChange.SetDefault();
+
beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0;
finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0);
}
@@ -48,20 +64,11 @@ namespace osu.Game.Rulesets.Mods
applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime);
}
- private double lastAdjust = 1;
-
///
/// Adjust the rate along the specified ramp
///
/// The amount of adjustment to apply (from 0..1).
- private void applyAdjustment(double amount)
- {
- double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment));
-
- track.Tempo.Value /= lastAdjust;
- track.Tempo.Value *= adjust;
-
- lastAdjust = adjust;
- }
+ private void applyAdjustment(double amount) =>
+ SpeedChange.Value = 1 + (FinalRate.Value - 1) * Math.Clamp(amount, 0, 1);
}
}
diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs
index b2e3abb59d..5416f1ac22 100644
--- a/osu.Game/Rulesets/Mods/ModWindDown.cs
+++ b/osu.Game/Rulesets/Mods/ModWindDown.cs
@@ -3,7 +3,9 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
namespace osu.Game.Rulesets.Mods
{
@@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.ChevronCircleDown;
public override double ScoreMultiplier => 1.0;
- protected override double FinalRateAdjustment => -0.25;
+ [SettingSource("Final rate", "The speed increase to ramp towards")]
+ public override BindableNumber FinalRate { get; } = new BindableDouble
+ {
+ MinValue = 0.5,
+ MaxValue = 0.99,
+ Default = 0.75,
+ Value = 0.75,
+ Precision = 0.01,
+ };
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
}
diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs
index 8df35a1de2..3cf584f3dd 100644
--- a/osu.Game/Rulesets/Mods/ModWindUp.cs
+++ b/osu.Game/Rulesets/Mods/ModWindUp.cs
@@ -3,7 +3,9 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
namespace osu.Game.Rulesets.Mods
{
@@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.ChevronCircleUp;
public override double ScoreMultiplier => 1.0;
- protected override double FinalRateAdjustment => 0.5;
+ [SettingSource("Final rate", "The speed increase to ramp towards")]
+ public override BindableNumber FinalRate { get; } = new BindableDouble
+ {
+ MinValue = 1.01,
+ MaxValue = 2,
+ Default = 1.5,
+ Value = 1.5,
+ Precision = 0.01,
+ };
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
}
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 1179efaa6e..bd96441ebb 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects
ApplyDefaultsToSelf(controlPointInfo, difficulty);
// This is done here since ApplyDefaultsToSelf may be used to determine the end time
- SampleControlPoint = controlPointInfo.SamplePointAt(((this as IHasEndTime)?.EndTime ?? StartTime) + control_point_leniency);
+ SampleControlPoint = controlPointInfo.SamplePointAt(this.GetEndTime() + control_point_leniency);
nestedHitObjects.Clear();
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index b5b1e26486..7fddb442d1 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
result = CreateSlider(pos, combo, comboOffset, convertControlPoints(points, pathType), length, repeatCount, nodeSamples);
// The samples are played when the slider ends, which is the last node
- result.Samples = nodeSamples[nodeSamples.Count - 1];
+ result.Samples = nodeSamples[^1];
}
else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
@@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
if (vertices[i] == vertices[i - 1])
{
- points[points.Count - 1].Type.Value = type;
+ points[^1].Type.Value = type;
continue;
}
diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs
index 86deba3b93..293138097f 100644
--- a/osu.Game/Rulesets/Objects/SliderPath.cs
+++ b/osu.Game/Rulesets/Objects/SliderPath.cs
@@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Objects
get
{
ensureValid();
- return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1];
+ return cumulativeLength.Count == 0 ? 0 : cumulativeLength[^1];
}
}
@@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Objects
if (calculatedLength > expectedDistance)
{
// The path will be shortened further, in which case we should trim any more unnecessary lengths and their associated path segments
- while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] >= expectedDistance)
+ while (cumulativeLength.Count > 0 && cumulativeLength[^1] >= expectedDistance)
{
cumulativeLength.RemoveAt(cumulativeLength.Count - 1);
calculatedPath.RemoveAt(pathEndIndex--);
@@ -269,7 +269,7 @@ namespace osu.Game.Rulesets.Objects
// The direction of the segment to shorten or lengthen
Vector2 dir = (calculatedPath[pathEndIndex] - calculatedPath[pathEndIndex - 1]).Normalized();
- calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[cumulativeLength.Count - 1]);
+ calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[^1]);
cumulativeLength.Add(expectedDistance);
}
}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index dd1b3615c7..45aa904b98 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets
/// The s to apply.
/// Unable to successfully load the beatmap to be usable with this ruleset.
///
- public abstract DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods);
+ public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null);
///
/// Creates a to convert a to one that is applicable for this .
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 10657f6b39..5033fd0686 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.UI
///
/// The beatmap.
///
- public Beatmap Beatmap;
+ public readonly Beatmap Beatmap;
public override IEnumerable Objects => Beatmap.HitObjects;
@@ -118,20 +118,22 @@ namespace osu.Game.Rulesets.UI
/// Creates a ruleset visualisation for the provided ruleset and beatmap.
///
/// The ruleset being represented.
- /// The beatmap to create the hit renderer for.
+ /// The beatmap to create the hit renderer for.
/// The s to apply.
- protected DrawableRuleset(Ruleset ruleset, IWorkingBeatmap workingBeatmap, IReadOnlyList mods)
+ protected DrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset)
{
- if (workingBeatmap == null)
- throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap));
+ if (beatmap == null)
+ throw new ArgumentNullException(nameof(beatmap), "Beatmap cannot be null.");
- this.mods = mods.ToArray();
+ if (!(beatmap is Beatmap tBeatmap))
+ throw new ArgumentException($"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}.", nameof(beatmap));
+
+ Beatmap = tBeatmap;
+ this.mods = mods?.ToArray() ?? Array.Empty();
RelativeSizeAxes = Axes.Both;
- Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
-
KeyBindingInputManager = CreateInputManager();
playfield = new Lazy(CreatePlayfield);
diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
index cf714b5d46..fda1d7c723 100644
--- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
@@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Cached(Type = typeof(IScrollingInfo))]
private readonly LocalScrollingInfo scrollingInfo;
- protected DrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ protected DrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
scrollingInfo = new LocalScrollingInfo();
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
index 7b68460e6b..1ab3a5b533 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
@@ -38,6 +38,8 @@ namespace osu.Game.Screens.Backgrounds
///
public readonly Bindable BlurAmount = new Bindable();
+ internal readonly IBindable IsBreakTime = new Bindable();
+
private readonly DimmableBackground dimmable;
protected virtual DimmableBackground CreateFadeContainer() => new DimmableBackground { RelativeSizeAxes = Axes.Both };
@@ -48,6 +50,7 @@ namespace osu.Game.Screens.Backgrounds
InternalChild = dimmable = CreateFadeContainer();
dimmable.EnableUserDim.BindTo(EnableUserDim);
+ dimmable.IsBreakTime.BindTo(IsBreakTime);
dimmable.BlurAmount.BindTo(BlurAmount);
}
diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
deleted file mode 100644
index 4b8720fe1c..0000000000
--- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Framework.Audio.Track;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Skinning;
-using osu.Game.Storyboards;
-
-namespace osu.Game.Screens.Edit
-{
- ///
- /// Encapsulates a while providing an overridden .
- ///
- ///
- public class EditorWorkingBeatmap : IWorkingBeatmap
- where TObject : HitObject
- {
- private readonly Beatmap playableBeatmap;
- private readonly WorkingBeatmap workingBeatmap;
-
- public EditorWorkingBeatmap(Beatmap playableBeatmap, WorkingBeatmap workingBeatmap)
- {
- this.playableBeatmap = playableBeatmap;
- this.workingBeatmap = workingBeatmap;
- }
-
- public IBeatmap Beatmap => workingBeatmap.Beatmap;
-
- public Texture Background => workingBeatmap.Background;
-
- public VideoSprite Video => workingBeatmap.Video;
-
- public Track Track => workingBeatmap.Track;
-
- public Waveform Waveform => workingBeatmap.Waveform;
-
- public Storyboard Storyboard => workingBeatmap.Storyboard;
-
- public ISkin Skin => workingBeatmap.Skin;
-
- public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) => playableBeatmap;
- }
-}
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index 94165fe4b7..6394fb8d23 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -87,12 +87,12 @@ namespace osu.Game.Screens
public virtual float BackgroundParallaxAmount => 1;
+ public virtual bool AllowRateAdjustments => true;
+
public Bindable Beatmap { get; private set; }
public Bindable Ruleset { get; private set; }
- public virtual bool AllowRateAdjustments => true;
-
public Bindable> Mods { get; private set; }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs
index 115f4b7e1a..8d54829d49 100644
--- a/osu.Game/Screens/OsuScreenDependencies.cs
+++ b/osu.Game/Screens/OsuScreenDependencies.cs
@@ -26,16 +26,26 @@ namespace osu.Game.Screens
Beatmap = parent.Get>()?.GetBoundCopy();
if (Beatmap == null)
+ {
Cache(Beatmap = parent.Get>().BeginLease(false));
+ CacheAs(Beatmap);
+ }
Ruleset = parent.Get>()?.GetBoundCopy();
if (Ruleset == null)
+ {
Cache(Ruleset = parent.Get>().BeginLease(true));
+ CacheAs(Ruleset);
+ }
Mods = parent.Get>>()?.GetBoundCopy();
+
if (Mods == null)
+ {
Cache(Mods = parent.Get>>().BeginLease(true));
+ CacheAs(Mods);
+ }
}
else
{
diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs
index 2cc03ae453..9f46fddc5e 100644
--- a/osu.Game/Screens/Play/GameplayClockContainer.cs
+++ b/osu.Game/Screens/Play/GameplayClockContainer.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play
private readonly double firstHitObjectTime;
- public readonly Bindable UserPlaybackRate = new BindableDouble(1)
+ public readonly BindableNumber UserPlaybackRate = new BindableDouble(1)
{
Default = 1,
MinValue = 0.5,
@@ -73,7 +73,6 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both;
track = beatmap.Track;
- track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
@@ -120,7 +119,6 @@ namespace osu.Game.Screens.Play
Seek(startTime);
adjustableClock.ProcessFrame();
- UserPlaybackRate.ValueChanged += _ => updateRate();
}
public void Restart()
@@ -223,7 +221,8 @@ namespace osu.Game.Screens.Play
speedAdjustmentsApplied = true;
track.ResetSpeedAdjustments();
- track.Tempo.Value = UserPlaybackRate.Value;
+ track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
+ track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
foreach (var mod in mods.OfType())
mod.ApplyToTrack(track);
@@ -244,8 +243,6 @@ namespace osu.Game.Screens.Play
track.ResetSpeedAdjustments();
speedAdjustmentsApplied = false;
}
-
- track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
}
}
}
diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
index 54556f8648..6196ce4026 100644
--- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs
@@ -31,7 +31,8 @@ namespace osu.Game.Screens.Play.HUD
RelativeSizeAxes = Axes.Both;
- processor.NewJudgement += onNewJudgement;
+ if (processor != null)
+ processor.NewJudgement += onNewJudgement;
}
[BackgroundDependencyLoader]
@@ -96,7 +97,9 @@ namespace osu.Game.Screens.Play.HUD
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
- processor.NewJudgement -= onNewJudgement;
+
+ if (processor != null)
+ processor.NewJudgement -= onNewJudgement;
}
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 0f9edf5606..e2f362780d 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -23,8 +24,8 @@ namespace osu.Game.Screens.Play
{
public class HUDOverlay : Container
{
- private const int duration = 250;
- private const Easing easing = Easing.OutQuint;
+ private const float fade_duration = 400;
+ private const Easing fade_easing = Easing.Out;
public readonly KeyCounterDisplay KeyCounter;
public readonly RollingCounter ComboCounter;
@@ -43,8 +44,15 @@ namespace osu.Game.Screens.Play
private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList mods;
- private Bindable showHud;
+ ///
+ /// Whether the elements that can optionally be hidden should be visible.
+ ///
+ public Bindable ShowHud { get; } = new BindableBool();
+
+ private Bindable configShowHud;
+
private readonly Container visibilityContainer;
+
private readonly BindableBool replayLoaded = new BindableBool();
private static bool hasShownNotificationOnce;
@@ -53,6 +61,8 @@ namespace osu.Game.Screens.Play
private readonly Container topScoreContainer;
+ private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter };
+
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods)
{
this.scoreProcessor = scoreProcessor;
@@ -68,13 +78,12 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
+ HealthDisplay = CreateHealthDisplay(),
topScoreContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
- AutoSizeDuration = 200,
- AutoSizeEasing = Easing.Out,
Children = new Drawable[]
{
AccuracyCounter = CreateAccuracyCounter(),
@@ -82,19 +91,20 @@ namespace osu.Game.Screens.Play
ComboCounter = CreateComboCounter(),
},
},
- HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
HitErrorDisplay = CreateHitErrorDisplayOverlay(),
+ PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
}
},
- PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
AutoSizeAxes = Axes.Both,
+ LayoutDuration = fade_duration / 2,
+ LayoutEasing = fade_easing,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
@@ -108,34 +118,24 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
{
- BindProcessor(scoreProcessor);
- BindDrawableRuleset(drawableRuleset);
+ if (scoreProcessor != null)
+ BindProcessor(scoreProcessor);
- Progress.Objects = drawableRuleset.Objects;
- Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
- Progress.RequestSeek = time => RequestSeek(time);
- Progress.ReferenceClock = drawableRuleset.FrameStableClock;
+ if (drawableRuleset != null)
+ {
+ BindDrawableRuleset(drawableRuleset);
+
+ Progress.Objects = drawableRuleset.Objects;
+ Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
+ Progress.RequestSeek = time => RequestSeek(time);
+ Progress.ReferenceClock = drawableRuleset.FrameStableClock;
+ }
ModDisplay.Current.Value = mods;
- showHud = config.GetBindable(OsuSetting.ShowInterface);
- showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true);
+ configShowHud = config.GetBindable(OsuSetting.ShowInterface);
- ShowHealthbar.BindValueChanged(healthBar =>
- {
- if (healthBar.NewValue)
- {
- HealthDisplay.FadeIn(duration, easing);
- topScoreContainer.MoveToY(30, duration, easing);
- }
- else
- {
- HealthDisplay.FadeOut(duration, easing);
- topScoreContainer.MoveToY(0, duration, easing);
- }
- }, true);
-
- if (!showHud.Value && !hasShownNotificationOnce)
+ if (!configShowHud.Value && !hasShownNotificationOnce)
{
hasShownNotificationOnce = true;
@@ -144,12 +144,39 @@ namespace osu.Game.Screens.Play
Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab."
});
}
+
+ // start all elements hidden
+ hideTargets.ForEach(d => d.Hide());
}
+ public override void Hide() => throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead.");
+
protected override void LoadComplete()
{
base.LoadComplete();
+ ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, fade_duration, fade_easing)));
+
+ ShowHealthbar.BindValueChanged(healthBar =>
+ {
+ if (healthBar.NewValue)
+ {
+ HealthDisplay.FadeIn(fade_duration, fade_easing);
+ topScoreContainer.MoveToY(30, fade_duration, fade_easing);
+ }
+ else
+ {
+ HealthDisplay.FadeOut(fade_duration, fade_easing);
+ topScoreContainer.MoveToY(0, fade_duration, fade_easing);
+ }
+ }, true);
+
+ configShowHud.BindValueChanged(visible =>
+ {
+ if (!ShowHud.Disabled)
+ ShowHud.Value = visible.NewValue;
+ }, true);
+
replayLoaded.BindValueChanged(replayLoadedValueChanged, true);
}
@@ -189,7 +216,7 @@ namespace osu.Game.Screens.Play
switch (e.Key)
{
case Key.Tab:
- showHud.Value = !showHud.Value;
+ configShowHud.Value = !configShowHud.Value;
return true;
}
}
@@ -257,7 +284,7 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20, Right = 10 },
};
- protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.FirstAvailableHitWindows);
+ protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset?.FirstAvailableHitWindows);
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 9f4ca7d817..5735506775 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -114,26 +114,31 @@ namespace osu.Game.Screens.Play
Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();
- WorkingBeatmap working = loadBeatmap();
+ if (Beatmap.Value is DummyWorkingBeatmap)
+ return;
- if (working == null)
+ IBeatmap playableBeatmap = loadPlayableBeatmap();
+
+ if (playableBeatmap == null)
return;
sampleRestart = audio.Samples.Get(@"Gameplay/restart");
mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel);
+ DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);
+
ScoreProcessor = DrawableRuleset.CreateScoreProcessor();
ScoreProcessor.Mods.BindTo(Mods);
if (!ScoreProcessor.Mode.Disabled)
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
- InternalChild = GameplayClockContainer = new GameplayClockContainer(working, Mods.Value, DrawableRuleset.GameplayStartTime);
+ InternalChild = GameplayClockContainer = new GameplayClockContainer(Beatmap.Value, Mods.Value, DrawableRuleset.GameplayStartTime);
addUnderlayComponents(GameplayClockContainer);
- addGameplayComponents(GameplayClockContainer, working);
- addOverlayComponents(GameplayClockContainer, working);
+ addGameplayComponents(GameplayClockContainer, Beatmap.Value);
+ addOverlayComponents(GameplayClockContainer, Beatmap.Value);
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
@@ -250,36 +255,32 @@ namespace osu.Game.Screens.Play
&& !DrawableRuleset.HasReplayLoaded.Value
&& !breakOverlay.IsBreakTime.Value;
- private WorkingBeatmap loadBeatmap()
+ private IBeatmap loadPlayableBeatmap()
{
- WorkingBeatmap working = Beatmap.Value;
- if (working is DummyWorkingBeatmap)
- return null;
+ IBeatmap playable;
try
{
- var beatmap = working.Beatmap;
-
- if (beatmap == null)
+ if (Beatmap.Value.Beatmap == null)
throw new InvalidOperationException("Beatmap was not loaded");
- rulesetInfo = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
+ rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset;
ruleset = rulesetInfo.CreateInstance();
try
{
- DrawableRuleset = ruleset.CreateDrawableRulesetWith(working, Mods.Value);
+ playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value);
}
catch (BeatmapInvalidForRulesetException)
{
- // we may fail to create a DrawableRuleset if the beatmap cannot be loaded with the user's preferred ruleset
- // let's try again forcing the beatmap's ruleset.
- rulesetInfo = beatmap.BeatmapInfo.Ruleset;
+ // A playable beatmap may not be creatable with the user's preferred ruleset, so try using the beatmap's default ruleset
+ rulesetInfo = Beatmap.Value.BeatmapInfo.Ruleset;
ruleset = rulesetInfo.CreateInstance();
- DrawableRuleset = ruleset.CreateDrawableRulesetWith(Beatmap.Value, Mods.Value);
+
+ playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, Mods.Value);
}
- if (!DrawableRuleset.Objects.Any())
+ if (playable.HitObjects.Count == 0)
{
Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error);
return null;
@@ -292,7 +293,7 @@ namespace osu.Game.Screens.Play
return null;
}
- return working;
+ return playable;
}
private void performImmediateExit()
@@ -510,6 +511,11 @@ namespace osu.Game.Screens.Play
Background.EnableUserDim.Value = true;
Background.BlurAmount.Value = 0;
+ // bind component bindables.
+ Background.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
+ DimmableStoryboard.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
+ DimmableVideo.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
+
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index ec524043ee..4acc619753 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -587,13 +587,16 @@ namespace osu.Game.Screens.Select
switch (d)
{
case DrawableCarouselBeatmapSet set:
+ {
lastSet = set;
set.MoveToX(set.Item.State.Value == CarouselItemState.Selected ? -100 : 0, 500, Easing.OutExpo);
set.MoveToY(currentY, 750, Easing.OutExpo);
break;
+ }
case DrawableCarouselBeatmap beatmap:
+ {
if (beatmap.Item.State.Value == CarouselItemState.Selected)
scrollTarget = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2;
@@ -619,6 +622,7 @@ namespace osu.Game.Screens.Select
}
break;
+ }
}
}
diff --git a/osu.Game/Skinning/LegacySkinResourceStore.cs b/osu.Game/Skinning/LegacySkinResourceStore.cs
index 72f3b9ed78..79a4e2e932 100644
--- a/osu.Game/Skinning/LegacySkinResourceStore.cs
+++ b/osu.Game/Skinning/LegacySkinResourceStore.cs
@@ -3,77 +3,39 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Threading.Tasks;
using osu.Framework.IO.Stores;
using osu.Game.Database;
namespace osu.Game.Skinning
{
- public class LegacySkinResourceStore : IResourceStore
+ public class LegacySkinResourceStore