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 3cd4dc48bf..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/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 18785d65ea..f67ca1213e 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -2,23 +2,21 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
- public class CatchScoreProcessor : ScoreProcessor
+ public class CatchScoreProcessor : ScoreProcessor
{
- public CatchScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public CatchScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
private float hpDrainRate;
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index 6b7f00c5d0..278ff97195 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -25,14 +25,14 @@ 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(this);
+ public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
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/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 49894a644c..a678ef60e7 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -3,13 +3,11 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring
{
- internal class ManiaScoreProcessor : ScoreProcessor
+ internal class ManiaScoreProcessor : ScoreProcessor
{
///
/// The hit HP multiplier at OD = 0.
@@ -51,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
///
private double hpMultiplier = 1;
- public ManiaScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public ManiaScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
@@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void SimulateAutoplay(IBeatmap beatmap)
{
while (true)
{
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index d371c1f7a8..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;
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
- public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(Beatmap);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
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 6e0745d125..6533aef8e3 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/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index affe18a30d..6779271cb3 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -5,22 +5,20 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
-using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Scoring
{
- internal class OsuScoreProcessor : ScoreProcessor
+ internal class OsuScoreProcessor : ScoreProcessor
{
- public OsuScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public OsuScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
private float hpDrainRate;
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
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 49aea52902..039d38e4fd 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -26,14 +26,14 @@ 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)
{
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
- public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(Beatmap);
protected override Playfield CreatePlayfield() => new OsuPlayfield();
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/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 75a27ff639..ae593d2e3a 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -1,15 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Taiko.Scoring
{
- internal class TaikoScoreProcessor : ScoreProcessor
+ internal class TaikoScoreProcessor : ScoreProcessor
{
///
/// A value used for calculating .
@@ -31,16 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Scoring
///
private double hpMissMultiplier;
- public TaikoScoreProcessor(DrawableRuleset drawableRuleset)
- : base(drawableRuleset)
+ public TaikoScoreProcessor(IBeatmap beatmap)
+ : base(beatmap)
{
}
- protected override void ApplyBeatmap(Beatmap beatmap)
+ protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
- hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
+ hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
}
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 fc109bf6a6..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;
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI
new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
}
- public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(Beatmap);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
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/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
index c0da605cdb..e708934bc3 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
@@ -68,9 +68,7 @@ namespace osu.Game.Tests.Visual.Online
};
AddStep("Set country", () => countryBindable.Value = country);
- AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddAssert("Check country is Null", () => countryBindable.Value == null);
AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
index 849ca2defc..0edf104da0 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
@@ -43,11 +43,6 @@ namespace osu.Game.Tests.Visual.Online
FullName = "United States"
};
- AddStep("Set country", () => countryBindable.Value = countryA);
- AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
- AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddAssert("Check country is Null", () => countryBindable.Value == null);
-
AddStep("Set country 1", () => countryBindable.Value = countryA);
AddStep("Set country 2", () => countryBindable.Value = countryB);
AddStep("Set null country", () => countryBindable.Value = null);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
new file mode 100644
index 0000000000..568e36df4c
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
@@ -0,0 +1,86 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Overlays.Rankings.Tables;
+using osu.Framework.Allocation;
+using osu.Game.Overlays;
+using NUnit.Framework;
+using osu.Game.Users;
+using osu.Framework.Bindables;
+using osu.Game.Overlays.Rankings;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsOverlay : OsuTestScene
+ {
+ protected override bool UseOnlineAPI => true;
+
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(PerformanceTable),
+ typeof(ScoresTable),
+ typeof(CountriesTable),
+ typeof(TableRowBackground),
+ typeof(UserBasedTable),
+ typeof(RankingsTable<>),
+ typeof(RankingsOverlay)
+ };
+
+ [Cached]
+ private RankingsOverlay rankingsOverlay;
+
+ private readonly Bindable countryBindable = new Bindable();
+ private readonly Bindable scope = new Bindable();
+
+ public TestSceneRankingsOverlay()
+ {
+ Add(rankingsOverlay = new TestRankingsOverlay
+ {
+ Country = { BindTarget = countryBindable },
+ Scope = { BindTarget = scope },
+ });
+ }
+
+ [Test]
+ public void TestShow()
+ {
+ AddStep("Show", rankingsOverlay.Show);
+ }
+
+ [Test]
+ public void TestFlagScopeDependency()
+ {
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+ AddStep("Set country", () => countryBindable.Value = us_country);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ }
+
+ [Test]
+ public void TestShowCountry()
+ {
+ AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country));
+ }
+
+ [Test]
+ public void TestHide()
+ {
+ AddStep("Hide", rankingsOverlay.Hide);
+ }
+
+ private static readonly Country us_country = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ private class TestRankingsOverlay : RankingsOverlay
+ {
+ public new Bindable Country => base.Country;
+
+ public new Bindable Scope => base.Scope;
+ }
+ }
+}
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/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 838b1c2f07..f8275ec4f6 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using osu.Framework.IO.File;
+using osu.Framework.Extensions;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats
switch (pair.Key)
{
case @"AudioFilename":
- metadata.AudioFile = FileSafety.PathStandardise(pair.Value);
+ metadata.AudioFile = pair.Value.ToStandardisedPath();
break;
case @"AudioLeadIn":
@@ -300,12 +300,12 @@ namespace osu.Game.Beatmaps.Formats
{
case EventType.Background:
string bgFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename);
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
break;
case EventType.Video:
string videoFilename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename);
+ beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
break;
case EventType.Break:
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 2b914669cb..e401e3fb97 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/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index f94ab3f27b..ccd46ab559 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -8,8 +8,8 @@ using System.IO;
using System.Linq;
using osuTK;
using osuTK.Graphics;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
-using osu.Framework.IO.File;
using osu.Game.IO;
using osu.Game.Storyboards;
@@ -335,6 +335,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
- private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"'));
+ private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath();
}
}
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 44d6d33cef..1255665cf0 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using osu.Game.Storyboards;
-using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
@@ -83,7 +82,10 @@ namespace osu.Game.Beatmaps
/// The absolute path of the output file.
public string Save()
{
- var path = FileSafety.GetTempPath(Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json");
+ string directory = Path.Combine(Path.GetTempPath(), @"osu!");
+ Directory.CreateDirectory(directory);
+
+ var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json");
using (var sw = new StreamWriter(path))
sw.WriteLine(Beatmap.Serialize());
return path;
@@ -97,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/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 7cce2fb92f..fd455d7cd5 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -13,7 +13,6 @@ using Microsoft.EntityFrameworkCore;
using osu.Framework;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.IO.File;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
@@ -493,7 +492,7 @@ namespace osu.Game.Database
{
fileInfos.Add(new TFileModel
{
- Filename = FileSafety.PathStandardise(file.Substring(prefix.Length)),
+ Filename = file.Substring(prefix.Length).ToStandardisedPath(),
FileInfo = files.Add(s)
});
}
diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs
index bb6bef1c50..1ed5fb3268 100644
--- a/osu.Game/Database/DatabaseContextFactory.cs
+++ b/osu.Game/Database/DatabaseContextFactory.cs
@@ -149,7 +149,15 @@ namespace osu.Game.Database
lock (writeLock)
{
recycleThreadContexts();
- storage.DeleteDatabase(database_name);
+
+ try
+ {
+ storage.DeleteDatabase(database_name);
+ }
+ catch
+ {
+ // for now we are not sure why file handles are kept open by EF, but this is generally only used in testing
+ }
}
}
}
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/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index 2721ce55dc..ab72276ad0 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -83,6 +83,15 @@ namespace osu.Game.Graphics.Containers
return base.OnDragEnd(e);
}
+ protected override bool OnScroll(ScrollEvent e)
+ {
+ // allow for controlling volume when alt is held.
+ // mostly for compatibility with osu-stable.
+ if (e.AltPressed) return false;
+
+ return base.OnScroll(e);
+ }
+
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction);
protected class OsuScrollbar : ScrollbarContainer
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/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
index 9c3eba9fdc..143d21e40d 100644
--- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
@@ -8,13 +8,14 @@ namespace osu.Game.Online.API.Requests
{
public class GetUserRankingsRequest : GetRankingsRequest
{
+ public readonly UserRankingsType Type;
+
private readonly string country;
- private readonly UserRankingsType type;
public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null)
: base(ruleset, page)
{
- this.type = type;
+ Type = type;
this.country = country;
}
@@ -28,7 +29,7 @@ namespace osu.Game.Online.API.Requests
return req;
}
- protected override string TargetPostfix() => type.ToString().ToLowerInvariant();
+ protected override string TargetPostfix() => Type.ToString().ToLowerInvariant();
}
public enum UserRankingsType
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/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs
index a1a893fa6b..b08a2a3900 100644
--- a/osu.Game/Overlays/Rankings/HeaderTitle.cs
+++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs
@@ -74,13 +74,7 @@ namespace osu.Game.Overlays.Rankings
base.LoadComplete();
}
- private void onScopeChanged(ValueChangedEvent scope)
- {
- scopeText.Text = scope.NewValue.ToString();
-
- if (scope.NewValue != RankingsScope.Performance)
- Country.Value = null;
- }
+ private void onScopeChanged(ValueChangedEvent scope) => scopeText.Text = scope.NewValue.ToString();
private void onCountryChanged(ValueChangedEvent country)
{
@@ -90,8 +84,6 @@ namespace osu.Game.Overlays.Rankings
return;
}
- Scope.Value = RankingsScope.Performance;
-
flag.Country = country.NewValue;
flag.Show();
}
diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs
new file mode 100644
index 0000000000..c8874ef891
--- /dev/null
+++ b/osu.Game/Overlays/RankingsOverlay.cs
@@ -0,0 +1,214 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+using osu.Game.Rulesets;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using System.Threading;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Rankings.Tables;
+
+namespace osu.Game.Overlays
+{
+ public class RankingsOverlay : FullscreenOverlay
+ {
+ protected readonly Bindable Country = new Bindable();
+ protected readonly Bindable Scope = new Bindable();
+ private readonly Bindable ruleset = new Bindable();
+
+ private readonly BasicScrollContainer scrollFlow;
+ private readonly Box background;
+ private readonly Container tableContainer;
+ private readonly DimmedLoadingLayer loading;
+
+ private APIRequest lastRequest;
+ private CancellationTokenSource cancellationToken;
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ public RankingsOverlay()
+ {
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ scrollFlow = new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarVisible = false,
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new RankingsHeader
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Country = { BindTarget = Country },
+ Scope = { BindTarget = Scope },
+ Ruleset = { BindTarget = ruleset }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ tableContainer = new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Margin = new MarginPadding { Vertical = 10 }
+ },
+ loading = new DimmedLoadingLayer(),
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colour)
+ {
+ Waves.FirstWaveColour = colour.Green;
+ Waves.SecondWaveColour = colour.GreenLight;
+ Waves.ThirdWaveColour = colour.GreenDark;
+ Waves.FourthWaveColour = colour.GreenDarker;
+
+ background.Colour = OsuColour.Gray(0.1f);
+ }
+
+ protected override void LoadComplete()
+ {
+ Country.BindValueChanged(_ =>
+ {
+ // if a country is requested, force performance scope.
+ if (Country.Value != null)
+ Scope.Value = RankingsScope.Performance;
+
+ Scheduler.AddOnce(loadNewContent);
+ }, true);
+
+ Scope.BindValueChanged(_ =>
+ {
+ // country filtering is only valid for performance scope.
+ if (Scope.Value != RankingsScope.Performance)
+ Country.Value = null;
+
+ Scheduler.AddOnce(loadNewContent);
+ }, true);
+
+ ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true);
+
+ base.LoadComplete();
+ }
+
+ public void ShowCountry(Country requested)
+ {
+ if (requested == null)
+ return;
+
+ Show();
+
+ Country.Value = requested;
+ }
+
+ private void loadNewContent()
+ {
+ loading.Show();
+
+ cancellationToken?.Cancel();
+ lastRequest?.Cancel();
+
+ var request = createScopedRequest();
+ lastRequest = request;
+
+ if (request == null)
+ {
+ loadTable(null);
+ return;
+ }
+
+ request.Success += () => loadTable(createTableFromResponse(request));
+ request.Failure += _ => loadTable(null);
+
+ api.Queue(request);
+ }
+
+ private APIRequest createScopedRequest()
+ {
+ switch (Scope.Value)
+ {
+ case RankingsScope.Performance:
+ return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
+
+ case RankingsScope.Country:
+ return new GetCountryRankingsRequest(ruleset.Value);
+
+ case RankingsScope.Score:
+ return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score);
+ }
+
+ return null;
+ }
+
+ private Drawable createTableFromResponse(APIRequest request)
+ {
+ switch (request)
+ {
+ case GetUserRankingsRequest userRequest:
+ switch (userRequest.Type)
+ {
+ case UserRankingsType.Performance:
+ return new PerformanceTable(1, userRequest.Result.Users);
+
+ case UserRankingsType.Score:
+ return new ScoresTable(1, userRequest.Result.Users);
+ }
+
+ return null;
+
+ case GetCountryRankingsRequest countryRequest:
+ return new CountriesTable(1, countryRequest.Result.Countries);
+ }
+
+ return null;
+ }
+
+ private void loadTable(Drawable table)
+ {
+ scrollFlow.ScrollToStart();
+
+ if (table == null)
+ {
+ tableContainer.Clear();
+ loading.Hide();
+ return;
+ }
+
+ LoadComponentAsync(table, t =>
+ {
+ loading.Hide();
+ tableContainer.Child = table;
+ }, (cancellationToken = new CancellationTokenSource()).Token);
+ }
+ }
+}
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/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 18c2a2ca01..a8a2294498 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -13,13 +13,16 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Scoring
{
- public abstract class ScoreProcessor
+ public class ScoreProcessor
{
+ private const double base_portion = 0.3;
+ private const double combo_portion = 0.7;
+ private const double max_score = 1000000;
+
///
/// Invoked when the is in a failed state.
/// This may occur regardless of whether an event is invoked.
@@ -67,11 +70,6 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly Bindable> Mods = new Bindable>(Array.Empty());
- ///
- /// Create a for this processor.
- ///
- public virtual HitWindows CreateHitWindows() => new HitWindows();
-
///
/// The current rank.
///
@@ -90,132 +88,23 @@ namespace osu.Game.Rulesets.Scoring
///
/// Whether all s have been processed.
///
- public virtual bool HasCompleted => false;
-
- ///
- /// The total number of judged s at the current point in time.
- ///
- public int JudgedHits { get; protected set; }
+ public bool HasCompleted => JudgedHits == MaxHits;
///
/// Whether this ScoreProcessor has already triggered the failed state.
///
- public virtual bool HasFailed { get; private set; }
+ public bool HasFailed { get; private set; }
///
- /// The default conditions for failing.
+ /// The maximum number of hits that can be judged.
///
- protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
-
- protected ScoreProcessor()
- {
- Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
- Accuracy.ValueChanged += delegate
- {
- Rank.Value = rankFrom(Accuracy.Value);
- foreach (var mod in Mods.Value.OfType())
- Rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value);
- };
- }
-
- private ScoreRank rankFrom(double acc)
- {
- if (acc == 1)
- return ScoreRank.X;
- if (acc > 0.95)
- return ScoreRank.S;
- if (acc > 0.9)
- return ScoreRank.A;
- if (acc > 0.8)
- return ScoreRank.B;
- if (acc > 0.7)
- return ScoreRank.C;
-
- return ScoreRank.D;
- }
-
- ///
- /// Resets this ScoreProcessor to a default state.
- ///
- /// Whether to store the current state of the for future use.
- protected virtual void Reset(bool storeResults)
- {
- TotalScore.Value = 0;
- Accuracy.Value = 1;
- Health.Value = 1;
- Combo.Value = 0;
- Rank.Value = ScoreRank.X;
- HighestCombo.Value = 0;
-
- JudgedHits = 0;
-
- HasFailed = false;
- }
-
- ///
- /// Checks if the score is in a failed state and notifies subscribers.
- ///