diff --git a/osu.Android.props b/osu.Android.props
index 3cd4dc48bf..239e1c2d31 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -54,6 +54,6 @@
-
+
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/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/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/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
index 3ed3f3e981..f53b64c729 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++
};
- foreach (var mod in Mods.Value.OfType())
+ foreach (var mod in SelectedMods.Value.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
index a0ab1908d6..dd863deed2 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs
@@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
- public TestSceneSpinnerHidden()
+ [SetUp]
+ public void SetUp() => Schedule(() =>
{
- Mods.Value = new[] { new OsuModHidden() };
- }
+ SelectedMods.Value = new[] { new OsuModHidden() };
+ });
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
new file mode 100644
index 0000000000..0fc441fec6
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs
@@ -0,0 +1,75 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Lines;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ ///
+ /// A visualisation of the line between two s.
+ ///
+ public class PathControlPointConnectionPiece : CompositeDrawable
+ {
+ public PathControlPoint ControlPoint;
+
+ private readonly Path path;
+ private readonly Slider slider;
+
+ private IBindable sliderPosition;
+ private IBindable pathVersion;
+
+ public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
+ {
+ this.slider = slider;
+ ControlPoint = controlPoint;
+
+ Origin = Anchor.Centre;
+ AutoSizeAxes = Axes.Both;
+
+ InternalChild = path = new SmoothPath
+ {
+ Anchor = Anchor.Centre,
+ PathRadius = 1
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ sliderPosition = slider.PositionBindable.GetBoundCopy();
+ sliderPosition.BindValueChanged(_ => updateConnectingPath());
+
+ pathVersion = slider.Path.Version.GetBoundCopy();
+ pathVersion.BindValueChanged(_ => updateConnectingPath());
+
+ updateConnectingPath();
+ }
+
+ ///
+ /// Updates the path connecting this control point to the next one.
+ ///
+ private void updateConnectingPath()
+ {
+ Position = slider.StackedPosition + ControlPoint.Position.Value;
+
+ path.ClearVertices();
+
+ int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
+
+ if (index == 0 || index == slider.Path.ControlPoints.Count)
+ return;
+
+ path.AddVertex(Vector2.Zero);
+ path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
+
+ path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index c2aefac587..6a0730db91 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
@@ -19,6 +18,9 @@ using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
+ ///
+ /// A visualisation of a single in a .
+ ///
public class PathControlPointPiece : BlueprintPiece
{
public Action RequestSelection;
@@ -28,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public readonly PathControlPoint ControlPoint;
private readonly Slider slider;
- private readonly Path path;
private readonly Container marker;
private readonly Drawable markerRing;
@@ -39,12 +40,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private OsuColour colours { get; set; }
private IBindable sliderPosition;
- private IBindable pathVersion;
+ private IBindable controlPointPosition;
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
-
ControlPoint = controlPoint;
Origin = Anchor.Centre;
@@ -52,11 +52,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChildren = new Drawable[]
{
- path = new SmoothPath
- {
- Anchor = Anchor.Centre,
- PathRadius = 1
- },
marker = new Container
{
Anchor = Anchor.Centre,
@@ -96,20 +91,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy();
- sliderPosition.BindValueChanged(_ => updateDisplay());
+ sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
- pathVersion = slider.Path.Version.GetBoundCopy();
- pathVersion.BindValueChanged(_ => updateDisplay());
+ controlPointPosition = ControlPoint.Position.GetBoundCopy();
+ controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
- updateDisplay();
- }
-
- private void updateDisplay()
- {
updateMarkerDisplay();
- updateConnectingPath();
}
// The connecting path is excluded from positional input
@@ -189,26 +178,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
colour = Color4.White;
marker.Colour = colour;
}
-
- ///
- /// Updates the path connecting this control point to the previous one.
- ///
- private void updateConnectingPath()
- {
- path.ClearVertices();
-
- int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
-
- if (index == -1)
- return;
-
- if (++index != slider.Path.ControlPoints.Count)
- {
- path.AddVertex(Vector2.Zero);
- path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
- }
-
- path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index cd19653a2e..6f583d7983 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
internal readonly Container Pieces;
+ private readonly Container connections;
+
private readonly Slider slider;
private readonly bool allowSelection;
@@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
RelativeSizeAxes = Axes.Both;
- InternalChild = Pieces = new Container { RelativeSizeAxes = Axes.Both };
+ InternalChildren = new Drawable[]
+ {
+ connections = new Container { RelativeSizeAxes = Axes.Both },
+ Pieces = new Container { RelativeSizeAxes = Axes.Both }
+ };
}
protected override void LoadComplete()
@@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
foreach (var point in controlPoints)
{
- var piece = new PathControlPointPiece(slider, point);
+ Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
+ {
+ if (allowSelection)
+ d.RequestSelection = selectPiece;
+ }));
- if (allowSelection)
- piece.RequestSelection = selectPiece;
-
- Pieces.Add(piece);
+ connections.Add(new PathControlPointConnectionPiece(slider, point));
}
}
private void removeControlPoints(IEnumerable controlPoints)
{
foreach (var point in controlPoints)
+ {
Pieces.RemoveAll(p => p.ControlPoint == point);
+ connections.RemoveAll(c => c.ControlPoint == point);
+ }
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game.Rulesets.Osu/Edit/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/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/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/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/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/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/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/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/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/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
index 7683bbcd63..e67cd94d5c 100644
--- a/osu.Game/Graphics/Containers/UserDimContainer.cs
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
+using osu.Game.Screens.Play;
namespace osu.Game.Graphics.Containers
{
@@ -14,7 +16,12 @@ namespace osu.Game.Graphics.Containers
///
public abstract class UserDimContainer : Container
{
- protected const float BACKGROUND_FADE_DURATION = 800;
+ ///
+ /// Amount of lightening to apply to current dim level during break times.
+ ///
+ public const float BREAK_LIGHTEN_AMOUNT = 0.3f;
+
+ protected const double BACKGROUND_FADE_DURATION = 800;
///
/// Whether or not user-configured dim levels should be applied to the container.
@@ -26,6 +33,12 @@ namespace osu.Game.Graphics.Containers
///
public readonly Bindable StoryboardReplacesBackground = new Bindable();
+ ///
+ /// Whether player is in break time.
+ /// Must be bound to to allow for dim adjustments in gameplay.
+ ///
+ public readonly IBindable IsBreakTime = new Bindable();
+
///
/// Whether the content of this container is currently being displayed.
///
@@ -33,11 +46,14 @@ namespace osu.Game.Graphics.Containers
protected Bindable UserDimLevel { get; private set; }
+ protected Bindable LightenDuringBreaks { get; private set; }
+
protected Bindable ShowStoryboard { get; private set; }
protected Bindable ShowVideo { get; private set; }
- protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0;
+ private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
+ protected float DimLevel => Math.Max(EnableUserDim.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
protected override Container Content => dimContent;
@@ -55,11 +71,14 @@ namespace osu.Game.Graphics.Containers
private void load(OsuConfigManager config)
{
UserDimLevel = config.GetBindable(OsuSetting.DimLevel);
+ LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks);
ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground);
EnableUserDim.ValueChanged += _ => UpdateVisuals();
UserDimLevel.ValueChanged += _ => UpdateVisuals();
+ LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
+ IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
@@ -84,7 +103,7 @@ namespace osu.Game.Graphics.Containers
ContentDisplayed = ShowDimContent;
dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
- dimContent.FadeColour(OsuColour.Gray(1 - (float)DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint);
+ dimContent.FadeColour(OsuColour.Gray(1f - DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index f310da3883..22b8d9d012 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -80,8 +80,13 @@ namespace osu.Game
// todo: move this to SongSelect once Screen has the ability to unsuspend.
[Cached]
- [Cached(Type = typeof(IBindable>))]
- protected readonly Bindable> Mods = new Bindable>(Array.Empty());
+ [Cached(typeof(IBindable>))]
+ protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
+
+ ///
+ /// Mods available for the current .
+ ///
+ public readonly Bindable>> AvailableMods = new Bindable>>();
protected Bindable Beatmap { get; private set; } // cached via load() method
@@ -233,6 +238,23 @@ namespace osu.Game
PreviewTrackManager previewTrackManager;
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
Add(previewTrackManager);
+
+ Ruleset.BindValueChanged(onRulesetChanged);
+ }
+
+ private void onRulesetChanged(ValueChangedEvent r)
+ {
+ var dict = new Dictionary>();
+
+ if (r.NewValue?.Available == true)
+ {
+ foreach (ModType type in Enum.GetValues(typeof(ModType)))
+ dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList();
+ }
+
+ if (!SelectedMods.Disabled)
+ SelectedMods.Value = Array.Empty();
+ AvailableMods.Value = dict;
}
protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer();
diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs
index f4b588ddb3..10b3bc7c2b 100644
--- a/osu.Game/Overlays/Mods/ModControlSection.cs
+++ b/osu.Game/Overlays/Mods/ModControlSection.cs
@@ -1,10 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
@@ -12,14 +12,13 @@ using osuTK;
namespace osu.Game.Overlays.Mods
{
- public class ModControlSection : Container
+ public class ModControlSection : CompositeDrawable
{
protected FillFlowContainer FlowContent;
- protected override Container Content => FlowContent;
public readonly Mod Mod;
- public ModControlSection(Mod mod)
+ public ModControlSection(Mod mod, IEnumerable modControls)
{
Mod = mod;
@@ -33,9 +32,8 @@ namespace osu.Game.Overlays.Mods
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
+ ChildrenEnumerable = modControls
};
-
- AddRange(Mod.CreateSettingsControls());
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index e8ea43e3f2..7f07ce620c 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -20,7 +20,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods.Sections;
-using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osuTK;
@@ -50,7 +49,7 @@ namespace osu.Game.Overlays.Mods
protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty());
- protected readonly IBindable Ruleset = new Bindable();
+ private Bindable>> availableMods;
protected Color4 LowMultiplierColour;
protected Color4 HighMultiplierColour;
@@ -322,14 +321,14 @@ namespace osu.Game.Overlays.Mods
}
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods)
+ private void load(OsuColour colours, AudioManager audio, Bindable> selectedMods, OsuGameBase osu)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
- Ruleset.BindTo(ruleset);
- if (mods != null) SelectedMods.BindTo(mods);
+ availableMods = osu.AvailableMods.GetBoundCopy();
+ SelectedMods.BindTo(selectedMods);
sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off");
@@ -360,7 +359,7 @@ namespace osu.Game.Overlays.Mods
{
base.LoadComplete();
- Ruleset.BindValueChanged(rulesetChanged, true);
+ availableMods.BindValueChanged(availableModsChanged, true);
SelectedMods.BindValueChanged(selectedModsChanged, true);
}
@@ -410,22 +409,12 @@ namespace osu.Game.Overlays.Mods
return base.OnKeyDown(e);
}
- private void rulesetChanged(ValueChangedEvent e)
+ private void availableModsChanged(ValueChangedEvent>> mods)
{
- if (e.NewValue == null) return;
-
- var instance = e.NewValue.CreateInstance();
+ if (mods.NewValue == null) return;
foreach (var section in ModSectionsContainer.Children)
- section.Mods = instance.GetModsFor(section.ModType);
-
- // attempt to re-select any already selected mods.
- // this may be the first time we are receiving the ruleset, in which case they will still match.
- selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value));
-
- // write the mods back to the SelectedMods bindable in the case a change was not applicable.
- // this generally isn't required as the previous line will perform deselection; just here for safety.
- refreshSelectedMods();
+ section.Mods = mods.NewValue[section.ModType];
}
private void selectedModsChanged(ValueChangedEvent> mods)
@@ -462,17 +451,17 @@ namespace osu.Game.Overlays.Mods
private void updateModSettings(ValueChangedEvent> selectedMods)
{
- foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue))
+ ModSettingsContent.Clear();
+
+ foreach (var mod in selectedMods.NewValue)
{
- var controls = added.CreateSettingsControls().ToList();
- if (controls.Count > 0)
- ModSettingsContent.Add(new ModControlSection(added) { Children = controls });
+ var settings = mod.CreateSettingsControls().ToList();
+ if (settings.Count > 0)
+ ModSettingsContent.Add(new ModControlSection(mod, settings));
}
- foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue))
- ModSettingsContent.RemoveAll(section => section.Mod == removed);
+ bool hasSettings = ModSettingsContent.Count > 0;
- bool hasSettings = ModSettingsContent.Children.Count > 0;
CustomiseButton.Enabled.Value = hasSettings;
if (!hasSettings)
@@ -502,8 +491,8 @@ namespace osu.Game.Overlays.Mods
{
base.Dispose(isDisposing);
- Ruleset.UnbindAll();
- SelectedMods.UnbindAll();
+ availableMods?.UnbindAll();
+ SelectedMods?.UnbindAll();
}
#endregion
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
index f4aa9a0144..3f8bc2b0c7 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs
@@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
KeyboardStep = 0.01f
},
new SettingsCheckbox
+ {
+ LabelText = "Lighten playfield during breaks",
+ Bindable = config.GetBindable(OsuSetting.LightenDuringBreaks)
+ },
+ new SettingsCheckbox
{
LabelText = "Show score overlay",
Bindable = config.GetBindable(OsuSetting.ShowInterface)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 9ac967ef74..22d94abcb9 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Edit
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
- private IWorkingBeatmap workingBeatmap;
private Beatmap playableBeatmap;
private IBeatmapProcessor beatmapProcessor;
@@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Edit
{
try
{
- drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap, Array.Empty()))
+ drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, playableBeatmap))
{
Clock = framedClock,
ProcessCustomClock = false
@@ -145,8 +144,7 @@ namespace osu.Game.Rulesets.Edit
{
var parentWorkingBeatmap = parent.Get>().Value;
- playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty());
- workingBeatmap = new EditorWorkingBeatmap(playableBeatmap, parentWorkingBeatmap);
+ playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo);
beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap);
@@ -250,7 +248,7 @@ namespace osu.Game.Rulesets.Edit
protected abstract IReadOnlyList CompositionTools { get; }
- protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods);
+ protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null);
public void BeginPlacement(HitObject hitObject)
{
diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs
index dcb3cb5597..71a666414f 100644
--- a/osu.Game/Rulesets/Mods/ModDaycore.cs
+++ b/osu.Game/Rulesets/Mods/ModDaycore.cs
@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Mods
@@ -13,9 +15,23 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.Question;
public override string Description => "Whoaaaaa...";
+ private readonly BindableNumber tempoAdjust = new BindableDouble(1);
+ private readonly BindableNumber freqAdjust = new BindableDouble(1);
+
+ protected ModDaycore()
+ {
+ SpeedChange.BindValueChanged(val =>
+ {
+ freqAdjust.Value = SpeedChange.Default;
+ tempoAdjust.Value = val.NewValue / SpeedChange.Default;
+ }, true);
+ }
+
public override void ApplyToTrack(Track track)
{
- track.Frequency.Value *= RateAdjust;
+ // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
+ track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
+ track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
index 5e685b040e..7015460c51 100644
--- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs
+++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs
@@ -3,12 +3,14 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModDoubleTime : ModTimeAdjust
+ public abstract class ModDoubleTime : ModRateAdjust
{
public override string Name => "Double Time";
public override string Acronym => "DT";
@@ -19,6 +21,14 @@ namespace osu.Game.Rulesets.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray();
- protected override double RateAdjust => 1.5;
+ [SettingSource("Speed increase", "The actual increase to apply")]
+ public override BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ MinValue = 1.01,
+ MaxValue = 2,
+ Default = 1.5,
+ Value = 1.5,
+ Precision = 0.01,
+ };
}
}
diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs
index d17ddd2253..15f7afa312 100644
--- a/osu.Game/Rulesets/Mods/ModHalfTime.cs
+++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs
@@ -3,12 +3,14 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHalfTime : ModTimeAdjust
+ public abstract class ModHalfTime : ModRateAdjust
{
public override string Name => "Half Time";
public override string Acronym => "HT";
@@ -19,6 +21,14 @@ namespace osu.Game.Rulesets.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray();
- protected override double RateAdjust => 0.75;
+ [SettingSource("Speed decrease", "The actual decrease to apply")]
+ public override BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ MinValue = 0.5,
+ MaxValue = 0.99,
+ Default = 0.75,
+ Value = 0.75,
+ Precision = 0.01,
+ };
}
}
diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs
index a4f1ef5a72..c14e02e64d 100644
--- a/osu.Game/Rulesets/Mods/ModNightcore.cs
+++ b/osu.Game/Rulesets/Mods/ModNightcore.cs
@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
@@ -14,9 +16,23 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => OsuIcon.ModNightcore;
public override string Description => "Uguuuuuuuu...";
+ private readonly BindableNumber tempoAdjust = new BindableDouble(1);
+ private readonly BindableNumber freqAdjust = new BindableDouble(1);
+
+ protected ModNightcore()
+ {
+ SpeedChange.BindValueChanged(val =>
+ {
+ freqAdjust.Value = SpeedChange.Default;
+ tempoAdjust.Value = val.NewValue / SpeedChange.Default;
+ }, true);
+ }
+
public override void ApplyToTrack(Track track)
{
- track.Frequency.Value *= RateAdjust;
+ // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
+ track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
+ track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs
similarity index 54%
rename from osu.Game/Rulesets/Mods/ModTimeAdjust.cs
rename to osu.Game/Rulesets/Mods/ModRateAdjust.cs
index 7d0cc2a7c3..1739524bcd 100644
--- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs
+++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs
@@ -1,20 +1,19 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeAdjust : Mod, IApplicableToTrack
+ public abstract class ModRateAdjust : Mod, IApplicableToTrack
{
- public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
-
- protected abstract double RateAdjust { get; }
+ public abstract BindableNumber SpeedChange { get; }
public virtual void ApplyToTrack(Track track)
{
- track.Tempo.Value *= RateAdjust;
+ track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
index 839b2ae36e..133f9ceb39 100644
--- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs
+++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs
@@ -3,42 +3,58 @@
using System;
using System.Linq;
+using osu.Framework.Audio;
using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap
+ public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToBeatmap, IApplicableToTrack
{
///
/// The point in the beatmap at which the final ramping rate should be reached.
///
private const double final_rate_progress = 0.75f;
- public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) };
-
- protected abstract double FinalRateAdjustment { get; }
+ [SettingSource("Final rate", "The final speed to ramp to")]
+ public abstract BindableNumber FinalRate { get; }
private double finalRateTime;
private double beginRampTime;
+
+ public BindableNumber SpeedChange { get; } = new BindableDouble
+ {
+ Default = 1,
+ Value = 1,
+ Precision = 0.01,
+ };
+
private Track track;
- public virtual void ApplyToTrack(Track track)
+ protected ModTimeRamp()
+ {
+ // for preview purpose at song select. eventually we'll want to be able to update every frame.
+ FinalRate.BindValueChanged(val => applyAdjustment(1), true);
+ }
+
+ public void ApplyToTrack(Track track)
{
this.track = track;
+ track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
- lastAdjust = 1;
-
- // for preview purposes. during gameplay, Update will overwrite this setting.
- applyAdjustment(1);
+ FinalRate.TriggerChange();
}
public virtual void ApplyToBeatmap(IBeatmap beatmap)
{
HitObject lastObject = beatmap.HitObjects.LastOrDefault();
+ SpeedChange.SetDefault();
+
beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0;
finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0);
}
@@ -48,20 +64,11 @@ namespace osu.Game.Rulesets.Mods
applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime);
}
- private double lastAdjust = 1;
-
///
/// Adjust the rate along the specified ramp
///
/// The amount of adjustment to apply (from 0..1).
- private void applyAdjustment(double amount)
- {
- double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment));
-
- track.Tempo.Value /= lastAdjust;
- track.Tempo.Value *= adjust;
-
- lastAdjust = adjust;
- }
+ private void applyAdjustment(double amount) =>
+ SpeedChange.Value = 1 + (FinalRate.Value - 1) * Math.Clamp(amount, 0, 1);
}
}
diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs
index b2e3abb59d..5416f1ac22 100644
--- a/osu.Game/Rulesets/Mods/ModWindDown.cs
+++ b/osu.Game/Rulesets/Mods/ModWindDown.cs
@@ -3,7 +3,9 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
namespace osu.Game.Rulesets.Mods
{
@@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.ChevronCircleDown;
public override double ScoreMultiplier => 1.0;
- protected override double FinalRateAdjustment => -0.25;
+ [SettingSource("Final rate", "The speed increase to ramp towards")]
+ public override BindableNumber FinalRate { get; } = new BindableDouble
+ {
+ MinValue = 0.5,
+ MaxValue = 0.99,
+ Default = 0.75,
+ Value = 0.75,
+ Precision = 0.01,
+ };
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
}
diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs
index 8df35a1de2..3cf584f3dd 100644
--- a/osu.Game/Rulesets/Mods/ModWindUp.cs
+++ b/osu.Game/Rulesets/Mods/ModWindUp.cs
@@ -3,7 +3,9 @@
using System;
using System.Linq;
+using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
namespace osu.Game.Rulesets.Mods
{
@@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage Icon => FontAwesome.Solid.ChevronCircleUp;
public override double ScoreMultiplier => 1.0;
- protected override double FinalRateAdjustment => 0.5;
+ [SettingSource("Final rate", "The speed increase to ramp towards")]
+ public override BindableNumber FinalRate { get; } = new BindableDouble
+ {
+ MinValue = 1.01,
+ MaxValue = 2,
+ Default = 1.5,
+ Value = 1.5,
+ Precision = 0.01,
+ };
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
}
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 1179efaa6e..bd96441ebb 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects
ApplyDefaultsToSelf(controlPointInfo, difficulty);
// This is done here since ApplyDefaultsToSelf may be used to determine the end time
- SampleControlPoint = controlPointInfo.SamplePointAt(((this as IHasEndTime)?.EndTime ?? StartTime) + control_point_leniency);
+ SampleControlPoint = controlPointInfo.SamplePointAt(this.GetEndTime() + control_point_leniency);
nestedHitObjects.Clear();
diff --git a/osu.Game/Rulesets/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.
- ///
- /// This can only ever notify subscribers once.
- ///
- ///
- protected void UpdateFailed(JudgementResult result)
- {
- if (HasFailed)
- return;
-
- if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
- return;
-
- if (Failed?.Invoke() != false)
- HasFailed = true;
- }
-
- ///
- /// Notifies subscribers of that a new judgement has occurred.
- ///
- /// The judgement scoring result to notify subscribers of.
- protected void NotifyNewJudgement(JudgementResult result)
- {
- NewJudgement?.Invoke(result);
-
- if (HasCompleted)
- AllJudged?.Invoke();
- }
-
- ///
- /// Retrieve a score populated with data for the current play this processor is responsible for.
- ///
- public virtual void PopulateScore(ScoreInfo score)
- {
- score.TotalScore = (long)Math.Round(TotalScore.Value);
- score.Combo = Combo.Value;
- score.MaxCombo = HighestCombo.Value;
- score.Accuracy = Math.Round(Accuracy.Value, 4);
- score.Rank = Rank.Value;
- score.Date = DateTimeOffset.Now;
-
- var hitWindows = CreateHitWindows();
-
- foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
- score.Statistics[result] = GetStatistic(result);
- }
-
- public abstract int GetStatistic(HitResult result);
-
- public abstract double GetStandardisedScore();
- }
-
- public class ScoreProcessor : ScoreProcessor
- where TObject : HitObject
- {
- private const double base_portion = 0.3;
- private const double combo_portion = 0.7;
- private const double max_score = 1000000;
-
- public sealed override bool HasCompleted => JudgedHits == MaxHits;
-
protected int MaxHits { get; private set; }
+ ///
+ /// The total number of judged s at the current point in time.
+ ///
+ public int JudgedHits { get; private set; }
+
private double maxHighestCombo;
private double maxBaseScore;
@@ -225,17 +114,22 @@ namespace osu.Game.Rulesets.Scoring
private double scoreMultiplier = 1;
- public ScoreProcessor(DrawableRuleset drawableRuleset)
+ public ScoreProcessor(IBeatmap beatmap)
{
Debug.Assert(base_portion + combo_portion == 1.0);
- drawableRuleset.OnNewResult += applyResult;
- drawableRuleset.OnRevertResult += revertResult;
+ Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
+ Accuracy.ValueChanged += accuracy =>
+ {
+ Rank.Value = rankFrom(accuracy.NewValue);
+ foreach (var mod in Mods.Value.OfType())
+ Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
+ };
- ApplyBeatmap(drawableRuleset.Beatmap);
+ ApplyBeatmap(beatmap);
Reset(false);
- SimulateAutoplay(drawableRuleset.Beatmap);
+ SimulateAutoplay(beatmap);
Reset(true);
if (maxBaseScore == 0 || maxHighestCombo == 0)
@@ -257,19 +151,19 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Applies any properties of the which affect scoring to this .
+ /// Applies any properties of the which affect scoring to this .
///
- /// The to read properties from.
- protected virtual void ApplyBeatmap(Beatmap beatmap)
+ /// The to read properties from.
+ protected virtual void ApplyBeatmap(IBeatmap beatmap)
{
}
///
- /// Simulates an autoplay of the to determine scoring values.
+ /// Simulates an autoplay of the to determine scoring values.
///
/// This provided temporarily. DO NOT USE.
- /// The to simulate.
- protected virtual void SimulateAutoplay(Beatmap beatmap)
+ /// The to simulate.
+ protected virtual void SimulateAutoplay(IBeatmap beatmap)
{
foreach (var obj in beatmap.HitObjects)
simulate(obj);
@@ -289,7 +183,7 @@ namespace osu.Game.Rulesets.Scoring
result.Type = judgement.MaxResult;
- applyResult(result);
+ ApplyResult(result);
}
}
@@ -297,22 +191,26 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a to this .
///
/// The to apply.
- private void applyResult(JudgementResult result)
+ public void ApplyResult(JudgementResult result)
{
- ApplyResult(result);
- updateScore();
+ ApplyResultInternal(result);
- UpdateFailed(result);
- NotifyNewJudgement(result);
+ updateScore();
+ updateFailed(result);
+
+ NewJudgement?.Invoke(result);
+
+ if (HasCompleted)
+ AllJudged?.Invoke();
}
///
/// Reverts the score change of a that was applied to this .
///
/// The judgement scoring result.
- private void revertResult(JudgementResult result)
+ public void RevertResult(JudgementResult result)
{
- RevertResult(result);
+ RevertResultInternal(result);
updateScore();
}
@@ -322,10 +220,10 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a to this .
///
///
- /// Any changes applied via this method can be reverted via .
+ /// Any changes applied via this method can be reverted via .
///
/// The to apply.
- protected virtual void ApplyResult(JudgementResult result)
+ protected virtual void ApplyResultInternal(JudgementResult result)
{
result.ComboAtJudgement = Combo.Value;
result.HighestComboAtJudgement = HighestCombo.Value;
@@ -372,10 +270,10 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Reverts the score change of a that was applied to this via .
+ /// Reverts the score change of a that was applied to this via .
///
/// The judgement scoring result.
- protected virtual void RevertResult(JudgementResult result)
+ protected virtual void RevertResultInternal(JudgementResult result)
{
Combo.Value = result.ComboAtJudgement;
HighestCombo.Value = result.HighestComboAtJudgement;
@@ -432,11 +330,49 @@ namespace osu.Game.Rulesets.Scoring
}
}
- public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
+ ///
+ /// Checks if the score is in a failed state and notifies subscribers.
+ ///
+ /// This can only ever notify subscribers once.
+ ///
+ ///
+ private void updateFailed(JudgementResult result)
+ {
+ if (HasFailed)
+ return;
- public override double GetStandardisedScore() => getScore(ScoringMode.Standardised);
+ if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
+ return;
- protected override void Reset(bool storeResults)
+ if (Failed?.Invoke() != false)
+ HasFailed = true;
+ }
+
+ 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;
+ }
+
+ public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
+
+ public double GetStandardisedScore() => getScore(ScoringMode.Standardised);
+
+ ///
+ /// Resets this ScoreProcessor to a default state.
+ ///
+ /// Whether to store the current state of the for future use.
+ protected virtual void Reset(bool storeResults)
{
scoreResultCounts.Clear();
@@ -447,13 +383,49 @@ namespace osu.Game.Rulesets.Scoring
maxBaseScore = baseScore;
}
- base.Reset(storeResults);
-
+ JudgedHits = 0;
baseScore = 0;
rollingMaxBaseScore = 0;
bonusScore = 0;
+
+ TotalScore.Value = 0;
+ Accuracy.Value = 1;
+ Health.Value = 1;
+ Combo.Value = 0;
+ Rank.Value = ScoreRank.X;
+ HighestCombo.Value = 0;
+
+ HasFailed = false;
}
+ ///
+ /// Retrieve a score populated with data for the current play this processor is responsible for.
+ ///
+ public virtual void PopulateScore(ScoreInfo score)
+ {
+ score.TotalScore = (long)Math.Round(TotalScore.Value);
+ score.Combo = Combo.Value;
+ score.MaxCombo = HighestCombo.Value;
+ score.Accuracy = Math.Round(Accuracy.Value, 4);
+ score.Rank = Rank.Value;
+ score.Date = DateTimeOffset.Now;
+
+ var hitWindows = CreateHitWindows();
+
+ foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
+ score.Statistics[result] = GetStatistic(result);
+ }
+
+ ///
+ /// The default conditions for failing.
+ ///
+ protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
+
+ ///
+ /// Create a for this processor.
+ ///
+ public virtual HitWindows CreateHitWindows() => new HitWindows();
+
///
/// Creates the that represents the scoring result for a .
///
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index a856974292..5033fd0686 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -45,6 +45,10 @@ namespace osu.Game.Rulesets.UI
public abstract class DrawableRuleset : DrawableRuleset, IProvideCursor, ICanAttachKeyCounter
where TObject : HitObject
{
+ public override event Action OnNewResult;
+
+ public override event Action OnRevertResult;
+
///
/// The selected variant.
///
@@ -91,20 +95,10 @@ namespace osu.Game.Rulesets.UI
}
}
- ///
- /// Invoked when a has been applied by a .
- ///
- public event Action OnNewResult;
-
- ///
- /// Invoked when a is being reverted by a .
- ///
- public event Action OnRevertResult;
-
///
/// The beatmap.
///
- public Beatmap Beatmap;
+ public readonly Beatmap Beatmap;
public override IEnumerable Objects => Beatmap.HitObjects;
@@ -124,20 +118,22 @@ namespace osu.Game.Rulesets.UI
/// Creates a ruleset visualisation for the provided ruleset and beatmap.
///
/// The ruleset being represented.
- /// The beatmap to create the hit renderer for.
+ /// The beatmap to create the hit renderer for.
/// The s to apply.
- protected DrawableRuleset(Ruleset ruleset, IWorkingBeatmap workingBeatmap, IReadOnlyList mods)
+ protected DrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset)
{
- if (workingBeatmap == null)
- throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap));
+ if (beatmap == null)
+ throw new ArgumentNullException(nameof(beatmap), "Beatmap cannot be null.");
- this.mods = mods.ToArray();
+ if (!(beatmap is Beatmap tBeatmap))
+ throw new ArgumentException($"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}.", nameof(beatmap));
+
+ Beatmap = tBeatmap;
+ this.mods = mods?.ToArray() ?? Array.Empty();
RelativeSizeAxes = Axes.Both;
- Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
-
KeyBindingInputManager = CreateInputManager();
playfield = new Lazy(CreatePlayfield);
@@ -309,7 +305,7 @@ namespace osu.Game.Rulesets.UI
/// The Playfield.
protected abstract Playfield CreatePlayfield();
- public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this);
+ public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(Beatmap);
///
/// Applies the active mods to this DrawableRuleset.
@@ -366,6 +362,16 @@ namespace osu.Game.Rulesets.UI
///
public abstract class DrawableRuleset : CompositeDrawable
{
+ ///
+ /// Invoked when a has been applied by a .
+ ///
+ public abstract event Action OnNewResult;
+
+ ///
+ /// Invoked when a is being reverted by a .
+ ///
+ public abstract event Action OnRevertResult;
+
///
/// Whether a replay is currently loaded.
///
diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
index cf714b5d46..fda1d7c723 100644
--- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
@@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Cached(Type = typeof(IScrollingInfo))]
private readonly LocalScrollingInfo scrollingInfo;
- protected DrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods)
+ protected DrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null)
: base(ruleset, beatmap, mods)
{
scrollingInfo = new LocalScrollingInfo();
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
index 7b68460e6b..1ab3a5b533 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
@@ -38,6 +38,8 @@ namespace osu.Game.Screens.Backgrounds
///
public readonly Bindable BlurAmount = new Bindable();
+ internal readonly IBindable IsBreakTime = new Bindable();
+
private readonly DimmableBackground dimmable;
protected virtual DimmableBackground CreateFadeContainer() => new DimmableBackground { RelativeSizeAxes = Axes.Both };
@@ -48,6 +50,7 @@ namespace osu.Game.Screens.Backgrounds
InternalChild = dimmable = CreateFadeContainer();
dimmable.EnableUserDim.BindTo(EnableUserDim);
+ dimmable.IsBreakTime.BindTo(IsBreakTime);
dimmable.BlurAmount.BindTo(BlurAmount);
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 33a4c48d28..1b4964c068 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -4,7 +4,6 @@
using System;
using osuTK.Graphics;
using osu.Framework.Screens;
-using osu.Game.Screens.Backgrounds;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -29,14 +28,13 @@ using osu.Game.Input.Bindings;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing;
+using osu.Game.Screens.Play;
using osu.Game.Users;
namespace osu.Game.Screens.Edit
{
- public class Editor : OsuScreen, IKeyBindingHandler
+ public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler
{
- protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
-
public override float BackgroundParallaxAmount => 0.1f;
public override bool AllowBackButton => false;
@@ -250,8 +248,12 @@ namespace osu.Game.Screens.Edit
{
base.OnEntering(last);
+ // todo: temporary. we want to be applying dim using the UserDimContainer eventually.
Background.FadeColour(Color4.DarkGray, 500);
+ Background.EnableUserDim.Value = false;
+ Background.BlurAmount.Value = 0;
+
resetTrack(true);
}
diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
deleted file mode 100644
index 4b8720fe1c..0000000000
--- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Framework.Audio.Track;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Skinning;
-using osu.Game.Storyboards;
-
-namespace osu.Game.Screens.Edit
-{
- ///
- /// Encapsulates a while providing an overridden .
- ///
- ///
- public class EditorWorkingBeatmap : IWorkingBeatmap
- where TObject : HitObject
- {
- private readonly Beatmap playableBeatmap;
- private readonly WorkingBeatmap workingBeatmap;
-
- public EditorWorkingBeatmap(Beatmap playableBeatmap, WorkingBeatmap workingBeatmap)
- {
- this.playableBeatmap = playableBeatmap;
- this.workingBeatmap = workingBeatmap;
- }
-
- public IBeatmap Beatmap => workingBeatmap.Beatmap;
-
- public Texture Background => workingBeatmap.Background;
-
- public VideoSprite Video => workingBeatmap.Video;
-
- public Track Track => workingBeatmap.Track;
-
- public Waveform Waveform => workingBeatmap.Waveform;
-
- public Storyboard Storyboard => workingBeatmap.Storyboard;
-
- public ISkin Skin => workingBeatmap.Skin;
-
- public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) => playableBeatmap;
- }
-}
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index 94165fe4b7..6394fb8d23 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -87,12 +87,12 @@ namespace osu.Game.Screens
public virtual float BackgroundParallaxAmount => 1;
+ public virtual bool AllowRateAdjustments => true;
+
public Bindable Beatmap { get; private set; }
public Bindable Ruleset { get; private set; }
- public virtual bool AllowRateAdjustments => true;
-
public Bindable> Mods { get; private set; }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs
index 115f4b7e1a..8d54829d49 100644
--- a/osu.Game/Screens/OsuScreenDependencies.cs
+++ b/osu.Game/Screens/OsuScreenDependencies.cs
@@ -26,16 +26,26 @@ namespace osu.Game.Screens
Beatmap = parent.Get>()?.GetBoundCopy();
if (Beatmap == null)
+ {
Cache(Beatmap = parent.Get>().BeginLease(false));
+ CacheAs(Beatmap);
+ }
Ruleset = parent.Get>()?.GetBoundCopy();
if (Ruleset == null)
+ {
Cache(Ruleset = parent.Get