diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 66db439c82..aa8848c55f 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -26,9 +26,9 @@
-
-
-
+
+
+
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs
new file mode 100644
index 0000000000..1e72591b87
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseOsuFlashlight.cs
@@ -0,0 +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 osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseOsuFlashlight : TestCaseOsuPlayer
+ {
+ protected override Player CreatePlayer(Ruleset ruleset)
+ {
+ Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
+
+ return base.CreatePlayer(ruleset);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs
index 76bd9ef758..9a32cf42b0 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderInput.cs
@@ -353,6 +353,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
+ protected override bool PauseOnFocusLost => false;
+
public ScoreAccessibleReplayPlayer(Score score)
: base(score, false, false)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs
index 2c40d18f1b..c0332fbf60 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs
@@ -1,23 +1,37 @@
// 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 System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModFlashlight : ModFlashlight
+ public class OsuModFlashlight : ModFlashlight, IApplicableToDrawableHitObjects
{
public override double ScoreMultiplier => 1.12;
private const float default_flashlight_size = 180;
- public override Flashlight CreateFlashlight() => new OsuFlashlight();
+ private OsuFlashlight flashlight;
+
+ public override Flashlight CreateFlashlight() => flashlight = new OsuFlashlight();
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var s in drawables.OfType())
+ {
+ s.Tracking.ValueChanged += flashlight.OnSliderTrackingChange;
+ }
+ }
private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition
{
@@ -26,6 +40,12 @@ namespace osu.Game.Rulesets.Osu.Mods
FlashlightSize = new Vector2(0, getSizeFor(0));
}
+ public void OnSliderTrackingChange(ValueChangedEvent e)
+ {
+ // If a slider is in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield over a brief duration.
+ this.TransformTo(nameof(FlashlightDim), e.NewValue ? 0.8f : 0.0f, 50);
+ }
+
protected override bool OnMouseMove(MouseMoveEvent e)
{
FlashlightPosition = e.MousePosition;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 374bc4bdd2..cce6dfe106 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
- ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss);
+ ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 57ea0abdd8..c1a4c1981f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -130,13 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- public bool Tracking;
+ public readonly Bindable Tracking = new Bindable();
protected override void Update()
{
base.Update();
- Tracking = Ball.Tracking;
+ Tracking.Value = Ball.Tracking;
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
diff --git a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs
index 81fab5b4b3..55e8a810fd 100644
--- a/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs
+++ b/osu.Game.Tests/Visual/Background/TestCaseBackgroundScreenBeatmap.cs
@@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
}
- private class TestPlayer : Player
+ private class TestPlayer : Visual.TestPlayer
{
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
diff --git a/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs b/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
index e2cf1ef28a..55c978ae06 100644
--- a/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
+++ b/osu.Game.Tests/Visual/Editor/TestCaseZoomableScrollContainer.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
+using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
@@ -18,38 +19,49 @@ namespace osu.Game.Tests.Visual.Editor
{
public class TestCaseZoomableScrollContainer : ManualInputManagerTestCase
{
- private readonly ZoomableScrollContainer scrollContainer;
- private readonly Drawable innerBox;
+ private ZoomableScrollContainer scrollContainer;
+ private Drawable innerBox;
- public TestCaseZoomableScrollContainer()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- Children = new Drawable[]
+ AddStep("Add new scroll container", () =>
{
- new Container
+ Children = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.X,
- Height = 250,
- Width = 0.75f,
- Children = new Drawable[]
+ new Container
{
- new Box
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Height = 250,
+ Width = 0.75f,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.Gray(30)
- },
- scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
- }
- },
- new MenuCursor()
- };
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(30)
+ },
+ scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
+ }
+ },
+ new MenuCursor()
+ };
- scrollContainer.Add(innerBox = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
+ scrollContainer.Add(innerBox = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
+ });
});
+ AddUntilStep("Scroll container is loaded", () => scrollContainer.LoadState >= LoadState.Loaded);
+ }
+
+ [Test]
+ public void TestWidthInitialization()
+ {
+ AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0);
}
[Test]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs
index 624e5f08bd..e5dc092c73 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCaseAutoplay.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0));
}
- private class ScoreAccessiblePlayer : Player
+ private class ScoreAccessiblePlayer : TestPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs
index 5cd01fe9a8..7d6430a2cc 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCaseFrameStabilityContainer.cs
@@ -73,6 +73,26 @@ namespace osu.Game.Tests.Visual.Gameplay
checkFrameCount(2);
}
+ [Test]
+ public void TestInitialSeekWithGameplayStart()
+ {
+ seekManualTo(1000);
+ createStabilityContainer(30000);
+
+ confirmSeek(1000);
+ checkFrameCount(0);
+
+ seekManualTo(10000);
+ confirmSeek(10000);
+
+ checkFrameCount(1);
+
+ seekManualTo(130000);
+ confirmSeek(130000);
+
+ checkFrameCount(6002);
+ }
+
[Test]
public void TestInitialSeek()
{
@@ -83,7 +103,11 @@ namespace osu.Game.Tests.Visual.Gameplay
checkFrameCount(0);
}
- private void createStabilityContainer() => AddStep("create container", () => mainContainer.Child = new FrameStabilityContainer().WithChild(consumer = new ClockConsumingChild()));
+ private const int max_frames_catchup = 50;
+
+ private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () =>
+ mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup }
+ .WithChild(consumer = new ClockConsumingChild()));
private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
index b9c7fd14bd..53ac990183 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCasePause.cs
@@ -6,6 +6,7 @@ using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
+using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets;
@@ -31,6 +32,14 @@ namespace osu.Game.Tests.Visual.Gameplay
base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both });
}
+ [SetUpSteps]
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+ AddStep("resume player", () => Player.GameplayClockContainer.Start());
+ confirmClockRunning(true);
+ }
+
[Test]
public void TestPauseResume()
{
@@ -157,6 +166,8 @@ namespace osu.Game.Tests.Visual.Gameplay
private void confirmPaused()
{
confirmClockRunning(false);
+ AddAssert("player not exited", () => Player.IsCurrentScreen());
+ AddAssert("player not failed", () => !Player.HasFailed);
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
}
@@ -184,7 +195,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer();
- protected class PausePlayer : Player
+ protected class PausePlayer : TestPlayer
{
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
@@ -196,10 +207,10 @@ namespace osu.Game.Tests.Visual.Gameplay
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
- protected override void LoadComplete()
+ public override void OnEntering(IScreen last)
{
- base.LoadComplete();
- HUDOverlay.HoldToQuit.PauseOnFocusLost = false;
+ base.OnEntering(last);
+ GameplayClockContainer.Stop();
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
index 15c38e6d18..b9a0421db7 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -34,20 +35,20 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLoadContinuation()
{
- AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false))));
+ Player player = null;
+ SlowLoadPlayer slowPlayer = null;
+
+ AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => player = new TestPlayer(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
- AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
+ AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
AddStep("load slow dummy beatmap", () =>
{
- SlowLoadPlayer slow = null;
-
- stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer(false, false)));
-
- Scheduler.AddDelayed(() => slow.Ready = true, 5000);
+ stack.Push(loader = new PlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false)));
+ Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000);
});
- AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
+ AddUntilStep("wait for player to be current", () => slowPlayer.IsCurrentScreen());
}
[Test]
@@ -101,19 +102,19 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
}
- private class TestPlayer : Player
+ private class TestPlayer : Visual.TestPlayer
{
public new Bindable> Mods => base.Mods;
- public TestPlayer()
- : base(false, false)
+ public TestPlayer(bool allowPause = true, bool showResults = true)
+ : base(allowPause, showResults)
{
}
}
- protected class SlowLoadPlayer : Player
+ protected class SlowLoadPlayer : Visual.TestPlayer
{
- public bool Ready;
+ public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false);
public SlowLoadPlayer(bool allowPause = true, bool showResults = true)
: base(allowPause, showResults)
@@ -123,8 +124,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[BackgroundDependencyLoader]
private void load()
{
- while (!Ready)
- Thread.Sleep(1);
+ if (!AllowLoad.Wait(TimeSpan.FromSeconds(10)))
+ throw new TimeoutException();
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs
index 263070ab21..a302c978d2 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCaseReplay.cs
@@ -33,6 +33,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
+ protected override bool PauseOnFocusLost => false;
+
public ScoreAccessibleReplayPlayer(Score score)
: base(score)
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
index 74114b2e53..6185fbd34e 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestCaseUpdateableBeatmapBackgroundSprite.cs
@@ -1,22 +1,27 @@
// 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 System.Linq;
+using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Tests.Beatmaps.IO;
+using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestCaseUpdateableBeatmapBackgroundSprite : OsuTestCase
{
- private TestUpdateableBeatmapBackgroundSprite backgroundSprite;
+ private BeatmapSetInfo testBeatmap;
+ private IAPIProvider api;
+ private RulesetStore rulesets;
[Resolved]
private BeatmapManager beatmaps { get; set; }
@@ -24,40 +29,119 @@ namespace osu.Game.Tests.Visual.UserInterface
[BackgroundDependencyLoader]
private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets)
{
- Bindable beatmapBindable = new Bindable();
+ this.api = api;
+ this.rulesets = rulesets;
- var imported = ImportBeatmapTest.LoadOszIntoOsu(osu);
+ testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu);
+ }
- Child = backgroundSprite = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
+ [Test]
+ public void TestNullBeatmap()
+ {
+ TestUpdateableBeatmapBackgroundSprite background = null;
- backgroundSprite.Beatmap.BindTo(beatmapBindable);
+ AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both });
+ AddUntilStep("wait for load", () => background.ContentLoaded);
+ }
- var req = new GetBeatmapSetRequest(1);
- api.Queue(req);
+ [Test]
+ public void TestLocalBeatmap()
+ {
+ TestUpdateableBeatmapBackgroundSprite background = null;
- AddStep("load null beatmap", () => beatmapBindable.Value = null);
- AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1);
- AddStep("load imported beatmap", () => beatmapBindable.Value = imported.Beatmaps.First());
- AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1);
+ AddStep("load local beatmap", () =>
+ {
+ Child = background = new TestUpdateableBeatmapBackgroundSprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ Beatmap = { Value = testBeatmap.Beatmaps.First() }
+ };
+ });
+ AddUntilStep("wait for load", () => background.ContentLoaded);
+ }
+
+ [Test]
+ public void TestOnlineBeatmap()
+ {
if (api.IsLoggedIn)
{
+ var req = new GetBeatmapSetRequest(1);
+ api.Queue(req);
+
AddUntilStep("wait for api response", () => req.Result != null);
- AddStep("load online beatmap", () => beatmapBindable.Value = new BeatmapInfo
+
+ TestUpdateableBeatmapBackgroundSprite background = null;
+
+ AddStep("load online beatmap", () =>
{
- BeatmapSet = req.Result?.ToBeatmapSet(rulesets)
+ Child = background = new TestUpdateableBeatmapBackgroundSprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
+ };
});
- AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1);
+
+ AddUntilStep("wait for load", () => background.ContentLoaded);
}
else
- {
AddStep("online (login first)", () => { });
- }
+ }
+
+ [Test]
+ public void TestUnloadAndReload()
+ {
+ var backgrounds = new List();
+ ScrollContainer scrollContainer = null;
+
+ AddStep("create backgrounds hierarchy", () =>
+ {
+ FillFlowContainer backgroundFlow;
+
+ Child = scrollContainer = new ScrollContainer
+ {
+ Size = new Vector2(500),
+ Child = backgroundFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(10),
+ Padding = new MarginPadding { Bottom = 550 }
+ }
+ };
+
+ for (int i = 0; i < 25; i++)
+ {
+ var background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
+
+ if (i % 2 == 0)
+ background.Beatmap.Value = testBeatmap.Beatmaps.First();
+
+ backgroundFlow.Add(new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 100,
+ Masking = true,
+ Child = background
+ });
+
+ backgrounds.Add(background);
+ }
+ });
+
+ var loadedBackgrounds = backgrounds.Where(b => b.ContentLoaded);
+
+ AddUntilStep("some loaded", () => loadedBackgrounds.Any());
+ AddStep("scroll to bottom", () => scrollContainer.ScrollToEnd());
+ AddUntilStep("all unloaded", () => !loadedBackgrounds.Any());
}
private class TestUpdateableBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite
{
- public int ChildCount => InternalChildren.Count;
+ protected override double UnloadDelay => 2000;
+
+ public bool ContentLoaded => ((DelayedLoadUnloadWrapper)InternalChildren.LastOrDefault())?.Content?.IsLoaded ?? false;
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
index f4b7b1d74f..a2279fdb14 100644
--- a/osu.Game/Beatmaps/BeatmapStore.cs
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -65,7 +65,6 @@ namespace osu.Game.Beatmaps
protected override IQueryable AddIncludesForDeletion(IQueryable query) =>
base.AddIncludesForDeletion(query)
.Include(s => s.Metadata)
- .Include(s => s.Beatmaps).ThenInclude(b => b.Scores)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
index ce7811fc0d..bd80919851 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
@@ -26,6 +26,11 @@ namespace osu.Game.Beatmaps.Drawables
this.beatmapSetCoverType = beatmapSetCoverType;
}
+ ///
+ /// Delay before the background is unloaded while off-screen.
+ ///
+ protected virtual double UnloadDelay => 10000;
+
private BeatmapInfo lastModel;
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Drawable content, double timeBeforeLoad)
@@ -34,13 +39,13 @@ namespace osu.Game.Beatmaps.Drawables
{
// If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was
// previously UNLOADED and thus its children have been disposed of, so we need to recreate them here.
- if (lastModel == Beatmap.Value && Beatmap.Value != null)
+ if (lastModel == Beatmap.Value)
return CreateDrawable(Beatmap.Value);
// If the model has changed since the previous unload (or if there was no load), then we can safely use the given content
lastModel = Beatmap.Value;
return content;
- }, timeBeforeLoad, 10000);
+ }, timeBeforeLoad, UnloadDelay);
}
protected override Drawable CreateDrawable(BeatmapInfo model)
diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
index 7cff54a058..856a5fefd4 100644
--- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs
+++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
@@ -29,5 +29,12 @@ namespace osu.Game.Beatmaps.Timing
/// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap.
///
public bool HasEffect => Duration >= MIN_BREAK_DURATION;
+
+ ///
+ /// Whether this break contains a specified time.
+ ///
+ /// The time to check in milliseconds.
+ /// Whether the time falls within this .
+ public bool Contains(double time) => time >= StartTime && time <= EndTime;
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index ba348c4090..54dbae9ddc 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -385,7 +385,7 @@ namespace osu.Game.Database
/// Delete multiple items.
/// This will post notifications tracking progress.
///
- public void Delete(List items)
+ public void Delete(List items, bool silent = false)
{
if (items.Count == 0) return;
@@ -396,7 +396,8 @@ namespace osu.Game.Database
State = ProgressNotificationState.Active,
};
- PostNotification?.Invoke(notification);
+ if (!silent)
+ PostNotification?.Invoke(notification);
int i = 0;
@@ -423,7 +424,7 @@ namespace osu.Game.Database
/// Restore multiple items that were previously deleted.
/// This will post notifications tracking progress.
///
- public void Undelete(List items)
+ public void Undelete(List items, bool silent = false)
{
if (!items.Any()) return;
@@ -434,7 +435,8 @@ namespace osu.Game.Database
State = ProgressNotificationState.Active,
};
- PostNotification?.Invoke(notification);
+ if (!silent)
+ PostNotification?.Invoke(notification);
int i = 0;
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index d62b53088a..594bc1e3ca 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -77,13 +77,13 @@ namespace osu.Game.Online.API
///
public void Register(IOnlineComponent component)
{
- Scheduler.Add(delegate { components.Add(component); });
+ Schedule(() => components.Add(component));
component.APIStateChanged(this, state);
}
public void Unregister(IOnlineComponent component)
{
- Scheduler.Add(delegate { components.Remove(component); });
+ Schedule(() => components.Remove(component));
}
public string AccessToken => authentication.RequestAccessToken();
@@ -274,7 +274,7 @@ namespace osu.Game.Online.API
state = value;
log.Add($@"We just went {state}!");
- Scheduler.Add(delegate
+ Schedule(() =>
{
components.ForEach(c => c.APIStateChanged(this, state));
OnStateChange?.Invoke(oldState, state);
@@ -352,9 +352,13 @@ namespace osu.Game.Online.API
public void Logout()
{
flushQueue();
+
password = null;
authentication.Clear();
- LocalUser.Value = createGuestUser();
+
+ // Scheduled prior to state change such that the state changed event is invoked with the correct user present
+ Schedule(() => LocalUser.Value = createGuestUser());
+
State = APIState.Offline;
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 44776bb2a8..66552c43e0 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -155,8 +155,23 @@ namespace osu.Game
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
+
+ // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
+ dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Host.Storage, contextFactory, Host));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap));
- dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, BeatmapManager, Host.Storage, contextFactory, Host));
+
+ // this should likely be moved to ArchiveModelManager when another case appers where it is necessary
+ // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to
+ // allow lookups to be done on the child (ScoreManager in this case) to perform the cascading delete.
+ List getBeatmapScores(BeatmapSetInfo set)
+ {
+ var beatmapIds = BeatmapManager.QueryBeatmaps(b => b.BeatmapSetInfoID == set.ID).Select(b => b.ID).ToList();
+ return ScoreManager.QueryScores(s => beatmapIds.Contains(s.Beatmap.ID)).ToList();
+ }
+
+ BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true);
+ BeatmapManager.ItemAdded += (i, existing) => ScoreManager.Undelete(getBeatmapScores(i), true);
+
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
index de710c5fcd..e41c90be45 100644
--- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
@@ -4,14 +4,12 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Scoring;
using osu.Game.Users;
@@ -185,8 +183,6 @@ namespace osu.Game.Overlays.Profile.Header
private class ScoreRankInfo : CompositeDrawable
{
- private readonly ScoreRank rank;
- private readonly Sprite rankSprite;
private readonly OsuSpriteText rankCount;
public int RankCount
@@ -196,8 +192,6 @@ namespace osu.Game.Overlays.Profile.Header
public ScoreRankInfo(ScoreRank rank)
{
- this.rank = rank;
-
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
@@ -206,10 +200,10 @@ namespace osu.Game.Overlays.Profile.Header
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- rankSprite = new Sprite
+ new DrawableRank(rank)
{
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit
+ RelativeSizeAxes = Axes.X,
+ Height = 30,
},
rankCount = new OsuSpriteText
{
@@ -220,12 +214,6 @@ namespace osu.Game.Overlays.Profile.Header
}
};
}
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}");
- }
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index 306d1b2a9c..e174a25df3 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -85,14 +85,15 @@ namespace osu.Game.Rulesets.Mods
Combo.ValueChanged += OnComboChange;
- this.FadeInFromZero(FLASHLIGHT_FADE_DURATION);
-
- foreach (var breakPeriod in Breaks)
+ using (BeginAbsoluteSequence(0))
{
- if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue;
+ foreach (var breakPeriod in Breaks)
+ {
+ if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue;
- this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION);
- this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION);
+ this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION);
+ this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION);
+ }
}
}
@@ -128,6 +129,20 @@ namespace osu.Game.Rulesets.Mods
}
}
+ private float flashlightDim;
+
+ public float FlashlightDim
+ {
+ get => flashlightDim;
+ set
+ {
+ if (flashlightDim == value) return;
+
+ flashlightDim = value;
+ Invalidate(Invalidation.DrawNode);
+ }
+ }
+
private class FlashlightDrawNode : DrawNode
{
protected new Flashlight Source => (Flashlight)base.Source;
@@ -136,6 +151,7 @@ namespace osu.Game.Rulesets.Mods
private Quad screenSpaceDrawQuad;
private Vector2 flashlightPosition;
private Vector2 flashlightSize;
+ private float flashlightDim;
public FlashlightDrawNode(Flashlight source)
: base(source)
@@ -149,7 +165,8 @@ namespace osu.Game.Rulesets.Mods
shader = Source.shader;
screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad;
flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix);
- flashlightSize = Vector2Extensions.Transform(Source.FlashlightSize, DrawInfo.Matrix);
+ flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
+ flashlightDim = Source.FlashlightDim;
}
public override void Draw(Action vertexAction)
@@ -160,6 +177,7 @@ namespace osu.Game.Rulesets.Mods
shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition);
shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize);
+ shader.GetUniform("flashlightDim").UpdateValue(ref flashlightDim);
Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs
index 4ac866fc90..9a5a4d4acd 100644
--- a/osu.Game/Rulesets/RulesetConfigCache.cs
+++ b/osu.Game/Rulesets/RulesetConfigCache.cs
@@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets
///
public class RulesetConfigCache : Component
{
- private readonly Dictionary configCache = new Dictionary();
+ private readonly ConcurrentDictionary configCache = new ConcurrentDictionary();
private readonly SettingsStore settingsStore;
public RulesetConfigCache(SettingsStore settingsStore)
@@ -34,10 +34,7 @@ namespace osu.Game.Rulesets
if (ruleset.RulesetInfo.ID == null)
throw new InvalidOperationException("The provided ruleset doesn't have a valid id.");
- if (configCache.TryGetValue(ruleset.RulesetInfo.ID.Value, out var existing))
- return existing;
-
- return configCache[ruleset.RulesetInfo.ID.Value] = ruleset.CreateConfig(settingsStore);
+ return configCache.GetOrAdd(ruleset.RulesetInfo.ID.Value, _ => ruleset.CreateConfig(settingsStore));
}
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 0ca92a8861..cf42a70640 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -360,6 +360,9 @@ namespace osu.Game.Rulesets.Scoring
JudgedHits--;
+ if (result.Type != HitResult.None)
+ scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
+
if (result.Judgement.IsBonus)
{
if (result.IsHit)
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 77d1e60b87..7db24d36a5 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
@@ -59,6 +60,8 @@ namespace osu.Game.Rulesets.UI
///
public Container Overlays { get; private set; }
+ public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
+
///
/// Invoked when a has been applied by a .
///
@@ -140,11 +143,11 @@ namespace osu.Game.Rulesets.UI
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new PlayfieldAdjustmentContainer();
[BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
+ private void load(OsuConfigManager config, CancellationToken? cancellationToken)
{
InternalChildren = new Drawable[]
{
- frameStabilityContainer = new FrameStabilityContainer
+ frameStabilityContainer = new FrameStabilityContainer(GameplayStartTime)
{
Child = KeyBindingInputManager
.WithChild(CreatePlayfieldAdjustmentContainer()
@@ -163,16 +166,21 @@ namespace osu.Game.Rulesets.UI
applyRulesetMods(mods, config);
- loadObjects();
+ loadObjects(cancellationToken);
}
///
/// Creates and adds drawable representations of hit objects to the play field.
///
- private void loadObjects()
+ private void loadObjects(CancellationToken? cancellationToken)
{
foreach (TObject h in Beatmap.HitObjects)
+ {
+ cancellationToken?.ThrowIfCancellationRequested();
addHitObject(h);
+ }
+
+ cancellationToken?.ThrowIfCancellationRequested();
Playfield.PostProcess();
@@ -334,6 +342,11 @@ namespace osu.Game.Rulesets.UI
///
public readonly BindableBool IsPaused = new BindableBool();
+ ///
+ /// The frame-stable clock which is being used for playfield display.
+ ///
+ public abstract GameplayClock FrameStableClock { get; }
+
/// ~
/// The associated ruleset.
///
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index ad15bcf057..1cc56fff8b 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -17,19 +17,29 @@ namespace osu.Game.Rulesets.UI
///
public class FrameStabilityContainer : Container, IHasReplayHandler
{
- public FrameStabilityContainer()
+ private readonly double gameplayStartTime;
+
+ ///
+ /// The number of frames (per parent frame) which can be run in an attempt to catch-up to real-time.
+ ///
+ public int MaxCatchUpFrames { get; set; } = 5;
+
+ [Cached]
+ public GameplayClock GameplayClock { get; }
+
+ public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{
RelativeSizeAxes = Axes.Both;
- gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
+
+ GameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
+
+ this.gameplayStartTime = gameplayStartTime;
}
private readonly ManualClock manualClock;
private readonly FramedClock framedClock;
- [Cached]
- private GameplayClock gameplayClock;
-
private IFrameBasedClock parentGameplayClock;
[BackgroundDependencyLoader(true)]
@@ -38,7 +48,7 @@ namespace osu.Game.Rulesets.UI
if (clock != null)
{
parentGameplayClock = clock;
- gameplayClock.IsPaused.BindTo(clock.IsPaused);
+ GameplayClock.IsPaused.BindTo(clock.IsPaused);
}
}
@@ -64,8 +74,6 @@ namespace osu.Game.Rulesets.UI
private bool isAttached => ReplayInputHandler != null;
- private const int max_catch_up_updates_per_frame = 50;
-
private const double sixty_frame_time = 1000.0 / 60;
private bool firstConsumption = true;
@@ -73,11 +81,11 @@ namespace osu.Game.Rulesets.UI
public override bool UpdateSubTree()
{
requireMoreUpdateLoops = true;
- validState = !gameplayClock.IsPaused.Value;
+ validState = !GameplayClock.IsPaused.Value;
int loops = 0;
- while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
+ while (validState && requireMoreUpdateLoops && loops++ < MaxCatchUpFrames)
{
updateClock();
@@ -116,6 +124,8 @@ namespace osu.Game.Rulesets.UI
firstConsumption = false;
}
+ else if (manualClock.CurrentTime < gameplayStartTime)
+ manualClock.CurrentTime = newProposedTime = Math.Min(gameplayStartTime, newProposedTime);
else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
{
newProposedTime = newProposedTime > manualClock.CurrentTime
@@ -160,7 +170,7 @@ namespace osu.Game.Rulesets.UI
if (parentGameplayClock == null)
parentGameplayClock = Clock;
- Clock = gameplayClock;
+ Clock = GameplayClock;
ProcessCustomClock = false;
}
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 7a527bfc69..6b737dc734 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -25,9 +25,9 @@ namespace osu.Game.Scoring
protected override string ImportFromStablePath => "Replays";
private readonly RulesetStore rulesets;
- private readonly BeatmapManager beatmaps;
+ private readonly Func beatmaps;
- public ScoreManager(RulesetStore rulesets, BeatmapManager beatmaps, Storage storage, IDatabaseContextFactory contextFactory, IIpcHost importHost = null)
+ public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IDatabaseContextFactory contextFactory, IIpcHost importHost = null)
: base(storage, contextFactory, new ScoreStore(contextFactory, storage), importHost)
{
this.rulesets = rulesets;
@@ -43,7 +43,7 @@ namespace osu.Game.Scoring
{
try
{
- return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(stream).ScoreInfo;
+ return new DatabasedLegacyScoreParser(rulesets, beatmaps()).Parse(stream).ScoreInfo;
}
catch (LegacyScoreParser.BeatmapNotFoundException e)
{
@@ -53,7 +53,7 @@ namespace osu.Game.Scoring
}
}
- public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps, Files.Store);
+ public Score GetScore(ScoreInfo score) => new LegacyDatabasedScore(score, rulesets, beatmaps(), Files.Store);
public List GetAllUsableScores() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
index 9b00a3998d..829437d599 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
@@ -92,6 +92,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // This width only gets updated on the application of a transform, so this needs to be initialized here.
+ updateZoomedContentWidth();
+ }
+
protected override bool OnScroll(ScrollEvent e)
{
if (e.IsPrecise)
@@ -102,6 +110,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
return true;
}
+ private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom;
+
private float zoomTarget = 1;
private void setZoomTarget(float newZoom, float focusPoint)
@@ -163,7 +173,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
d.currentZoom = newZoom;
- d.zoomedContent.Width = d.DrawWidth * d.currentZoom;
+ d.updateZoomedContentWidth();
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
// TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is.
d.Invalidate(Invalidation.DrawSize);
diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
index 446df94aca..91c14591b1 100644
--- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
+++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs
@@ -25,6 +25,8 @@ namespace osu.Game.Screens.Play.HUD
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
+ public readonly Bindable IsPaused = new Bindable();
+
private readonly Button button;
public Action Action
@@ -51,7 +53,8 @@ namespace osu.Game.Screens.Play.HUD
button = new Button
{
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
- HoverLost = () => text.FadeOut(500, Easing.OutQuint)
+ HoverLost = () => text.FadeOut(500, Easing.OutQuint),
+ IsPaused = { BindTarget = IsPaused }
}
};
AutoSizeAxes = Axes.Both;
@@ -94,6 +97,8 @@ namespace osu.Game.Screens.Play.HUD
private CircularProgress circularProgress;
private Circle overlayCircle;
+ public readonly Bindable IsPaused = new Bindable();
+
protected override bool AllowMultipleFires => true;
public Action HoverGained;
@@ -217,7 +222,7 @@ namespace osu.Game.Screens.Play.HUD
private void updateActive()
{
- if (!pauseOnFocusLost) return;
+ if (!pauseOnFocusLost || IsPaused.Value) return;
if (gameActive.Value)
AbortConfirm();
diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs
index d7fc90e36d..0f3c92a962 100644
--- a/osu.Game/Screens/Play/HUD/ModDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Screens.Play.HUD
public bool DisplayUnrankedText = true;
+ public bool AllowExpand = true;
+
private readonly Bindable> current = new Bindable>();
public Bindable> Current
@@ -88,7 +90,9 @@ namespace osu.Game.Screens.Play.HUD
protected override void LoadComplete()
{
base.LoadComplete();
+
appearTransform();
+ iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
}
private void appearTransform()
@@ -98,14 +102,17 @@ namespace osu.Game.Screens.Play.HUD
else
unrankedText.Hide();
- iconsContainer.FinishTransforms();
- iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
expand();
+
using (iconsContainer.BeginDelayedSequence(1200))
contract();
}
- private void expand() => iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
+ private void expand()
+ {
+ if (AllowExpand)
+ iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
+ }
private void contract() => iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 3c1b33297a..017bf70133 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -35,6 +35,10 @@ namespace osu.Game.Screens.Play
public readonly HoldForMenuButton HoldToQuit;
public readonly PlayerSettingsOverlay PlayerSettingsOverlay;
+ private readonly ScoreProcessor scoreProcessor;
+ private readonly DrawableRuleset drawableRuleset;
+ private readonly IReadOnlyList mods;
+
private Bindable showHud;
private readonly Container visibilityContainer;
private readonly BindableBool replayLoaded = new BindableBool();
@@ -45,6 +49,10 @@ namespace osu.Game.Screens.Play
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods)
{
+ this.scoreProcessor = scoreProcessor;
+ this.drawableRuleset = drawableRuleset;
+ this.mods = mods;
+
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
@@ -89,20 +97,21 @@ namespace osu.Game.Screens.Play
}
}
};
+ }
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
+ {
BindProcessor(scoreProcessor);
BindDrawableRuleset(drawableRuleset);
Progress.Objects = drawableRuleset.Objects;
Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
Progress.RequestSeek = time => RequestSeek(time);
+ Progress.ReferenceClock = drawableRuleset.FrameStableClock;
ModDisplay.Current.Value = mods;
- }
- [BackgroundDependencyLoader(true)]
- private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
- {
showHud = config.GetBindable(OsuSetting.ShowInterface);
showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration);
showHud.TriggerChange();
@@ -122,8 +131,7 @@ namespace osu.Game.Screens.Play
{
base.LoadComplete();
- replayLoaded.ValueChanged += replayLoadedValueChanged;
- replayLoaded.TriggerChange();
+ replayLoaded.BindValueChanged(replayLoadedValueChanged, true);
}
private void replayLoadedValueChanged(ValueChangedEvent e)
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index df3cc99f31..8f6ed43206 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -43,6 +43,11 @@ namespace osu.Game.Screens.Play
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered;
+ ///
+ /// Whether gameplay should pause when the game window focus is lost.
+ ///
+ protected virtual bool PauseOnFocusLost => true;
+
public Action RestartRequested;
public bool HasFailed { get; private set; }
@@ -135,7 +140,11 @@ namespace osu.Game.Screens.Play
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value)
{
- HoldToQuit = { Action = performUserRequestedExit },
+ HoldToQuit =
+ {
+ Action = performUserRequestedExit,
+ IsPaused = { BindTarget = GameplayClockContainer.IsPaused }
+ },
PlayerSettingsOverlay = { PlaybackSettings = { UserPlaybackRate = { BindTarget = GameplayClockContainer.UserPlaybackRate } } },
KeyCounter = { Visible = { BindTarget = DrawableRuleset.HasReplayLoaded } },
RequestSeek = GameplayClockContainer.Seek,
@@ -170,6 +179,8 @@ namespace osu.Game.Screens.Play
}
};
+ DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true);
+
// bind clock into components that require it
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
@@ -406,8 +417,9 @@ namespace osu.Game.Screens.Play
IsResuming = true;
PauseOverlay.Hide();
- // time-based conditions may allow instant resume.
- if (GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
+ // breaks and time-based conditions may allow instant resume.
+ double time = GameplayClockContainer.GameplayClock.CurrentTime;
+ if (Beatmap.Value.Beatmap.Breaks.Any(b => b.Contains(time)) || time < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs
index 94b25e04a3..d478454f00 100644
--- a/osu.Game/Screens/Play/SongProgress.cs
+++ b/osu.Game/Screens/Play/SongProgress.cs
@@ -10,6 +10,7 @@ using osu.Game.Graphics;
using osu.Framework.Allocation;
using System.Linq;
using osu.Framework.Bindables;
+using osu.Framework.Timing;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
@@ -55,7 +56,9 @@ namespace osu.Game.Screens.Play
private readonly BindableBool replayLoaded = new BindableBool();
- private GameplayClock gameplayClock;
+ public IClock ReferenceClock;
+
+ private IClock gameplayClock;
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, GameplayClock clock)
@@ -154,10 +157,12 @@ namespace osu.Game.Screens.Play
if (objects == null)
return;
- double position = gameplayClock?.CurrentTime ?? Time.Current;
- double progress = Math.Min(1, (position - firstHitTime) / (lastHitTime - firstHitTime));
+ double gameplayTime = gameplayClock?.CurrentTime ?? Time.Current;
+ double frameStableTime = ReferenceClock?.CurrentTime ?? gameplayTime;
- bar.CurrentTime = position;
+ double progress = Math.Min(1, (frameStableTime - firstHitTime) / (lastHitTime - firstHitTime));
+
+ bar.CurrentTime = gameplayTime;
graph.Progress = (int)(graph.ColumnCount * progress);
}
}
diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs
index 2e7d452fe7..dd7b5826d5 100644
--- a/osu.Game/Screens/Play/SongProgressBar.cs
+++ b/osu.Game/Screens/Play/SongProgressBar.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
+using osu.Framework.MathUtils;
namespace osu.Game.Screens.Play
{
@@ -107,9 +108,17 @@ namespace osu.Game.Screens.Play
protected override void UpdateValue(float value)
{
- var xFill = value * UsableWidth;
- fill.Width = xFill;
- handleBase.X = xFill;
+ // handled in update
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ float newX = (float)Interpolation.Lerp(handleBase.X, NormalizedValue * UsableWidth, MathHelper.Clamp(Time.Elapsed / 40, 0, 1));
+
+ fill.Width = newX;
+ handleBase.X = newX;
}
protected override void OnUserChange(double value) => OnSeek?.Invoke(value);
diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs
index 03b9826e9b..6ba29751b0 100644
--- a/osu.Game/Screens/Select/Footer.cs
+++ b/osu.Game/Screens/Select/Footer.cs
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using osuTK;
using osuTK.Graphics;
-using osuTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -20,9 +19,6 @@ namespace osu.Game.Screens.Select
{
private readonly Box modeLight;
- private const float play_song_select_button_width = 100;
- private const float play_song_select_button_height = 50;
-
public const float HEIGHT = 50;
public const int TRANSITION_LENGTH = 300;
@@ -33,57 +29,36 @@ namespace osu.Game.Screens.Select
private readonly FillFlowContainer buttons;
- /// Text on the button.
- /// Colour of the button.
- /// Hotkey of the button.
- /// Action the button does.
- ///
- /// Higher depth to be put on the left, and lower to be put on the right.
- /// Notice this is different to !
- ///
- public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
- {
- var button = new FooterButton
- {
- Text = text,
- Height = play_song_select_button_height,
- Width = play_song_select_button_width,
- Depth = depth,
- SelectedColour = colour,
- DeselectedColour = colour.Opacity(0.5f),
- Hotkey = hotkey,
- Hovered = updateModeLight,
- HoverLost = updateModeLight,
- Action = action,
- };
-
- buttons.Add(button);
- buttons.SetLayoutPosition(button, -depth);
- }
-
private readonly List overlays = new List();
- /// Text on the button.
- /// Colour of the button.
- /// Hotkey of the button.
+ /// THe button to be added.
/// The to be toggled by this button.
- ///
- /// Higher depth to be put on the left, and lower to be put on the right.
- /// Notice this is different to !
- ///
- public void AddButton(string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0)
+ public void AddButton(FooterButton button, OverlayContainer overlay)
{
overlays.Add(overlay);
- AddButton(text, colour, () =>
+ button.Action = () => showOverlay(overlay);
+
+ AddButton(button);
+ }
+
+ /// Button to be added.
+ public void AddButton(FooterButton button)
+ {
+ button.Hovered = updateModeLight;
+ button.HoverLost = updateModeLight;
+
+ buttons.Add(button);
+ }
+
+ private void showOverlay(OverlayContainer overlay)
+ {
+ foreach (var o in overlays)
{
- foreach (var o in overlays)
- {
- if (o == overlay)
- o.ToggleVisibility();
- else
- o.Hide();
- }
- }, hotkey, depth);
+ if (o == overlay)
+ o.ToggleVisibility();
+ else
+ o.Hide();
+ }
}
private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, Easing.OutQuint);
diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs
index 9b98e344ce..e18a086a10 100644
--- a/osu.Game/Screens/Select/FooterButton.cs
+++ b/osu.Game/Screens/Select/FooterButton.cs
@@ -6,6 +6,7 @@ using osuTK;
using osuTK.Graphics;
using osuTK.Input;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
@@ -20,11 +21,11 @@ namespace osu.Game.Screens.Select
public string Text
{
- get => spriteText?.Text;
+ get => SpriteText?.Text;
set
{
- if (spriteText != null)
- spriteText.Text = value;
+ if (SpriteText != null)
+ SpriteText.Text = value;
}
}
@@ -53,7 +54,8 @@ namespace osu.Game.Screens.Select
}
}
- private readonly SpriteText spriteText;
+ protected readonly Container TextContainer;
+ protected readonly SpriteText SpriteText;
private readonly Box box;
private readonly Box light;
@@ -61,8 +63,18 @@ namespace osu.Game.Screens.Select
public FooterButton()
{
+ AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
+ TextContainer = new Container
+ {
+ Size = new Vector2(100, 50),
+ Child = SpriteText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ },
box = new Box
{
RelativeSizeAxes = Axes.Both,
@@ -78,11 +90,6 @@ namespace osu.Game.Screens.Select
EdgeSmoothness = new Vector2(2, 0),
RelativeSizeAxes = Axes.X,
},
- spriteText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- }
};
}
diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs
new file mode 100644
index 0000000000..c96c5022c0
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonMods.cs
@@ -0,0 +1,60 @@
+// 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.Game.Screens.Play.HUD;
+using osu.Game.Rulesets.Mods;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Game.Graphics;
+using osuTK;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonMods : FooterButton
+ {
+ public FooterButtonMods(Bindable> mods)
+ {
+ FooterModDisplay modDisplay;
+
+ Add(new Container
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Child = modDisplay = new FooterModDisplay
+ {
+ DisplayUnrankedText = false,
+ Scale = new Vector2(0.8f)
+ },
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Left = 70 }
+ });
+
+ if (mods != null)
+ modDisplay.Current = mods;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Yellow;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"mods";
+ Hotkey = Key.F1;
+ }
+
+ private class FooterModDisplay : ModDisplay
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false;
+
+ public FooterModDisplay()
+ {
+ AllowExpand = false;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/FooterButtonOptions.cs b/osu.Game/Screens/Select/FooterButtonOptions.cs
new file mode 100644
index 0000000000..c000d8a8c8
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonOptions.cs
@@ -0,0 +1,22 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Game.Graphics;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonOptions : FooterButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Blue;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"options";
+ Hotkey = Key.F3;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs
new file mode 100644
index 0000000000..14c9eb2035
--- /dev/null
+++ b/osu.Game/Screens/Select/FooterButtonRandom.cs
@@ -0,0 +1,68 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK.Input;
+
+namespace osu.Game.Screens.Select
+{
+ public class FooterButtonRandom : FooterButton
+ {
+ private readonly SpriteText secondaryText;
+ private bool secondaryActive;
+
+ public FooterButtonRandom()
+ {
+ TextContainer.Add(secondaryText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = @"rewind",
+ Alpha = 0
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ SelectedColour = colours.Green;
+ DeselectedColour = SelectedColour.Opacity(0.5f);
+ Text = @"random";
+ Hotkey = Key.F2;
+ }
+
+ protected override bool OnKeyDown(KeyDownEvent e)
+ {
+ secondaryActive = e.ShiftPressed;
+ updateText();
+ return base.OnKeyDown(e);
+ }
+
+ protected override bool OnKeyUp(KeyUpEvent e)
+ {
+ secondaryActive = e.ShiftPressed;
+ updateText();
+ return base.OnKeyUp(e);
+ }
+
+ private void updateText()
+ {
+ if (secondaryActive)
+ {
+ SpriteText.FadeOut(120, Easing.InQuad);
+ secondaryText.FadeIn(120, Easing.InQuad);
+ }
+ else
+ {
+ SpriteText.FadeIn(120, Easing.InQuad);
+ secondaryText.FadeOut(120, Easing.InQuad);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 14c362b8ca..fed1f7a944 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -39,6 +39,7 @@ namespace osu.Game.Screens.Select
public abstract class SongSelect : OsuScreen
{
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245);
+
protected const float BACKGROUND_BLUR = 20;
private const float left_area_padding = 20;
@@ -89,8 +90,6 @@ namespace osu.Game.Screens.Select
protected SongSelect()
{
- const float carousel_width = 640;
-
AddRangeInternal(new Drawable[]
{
new ParallaxContainer
@@ -103,7 +102,8 @@ namespace osu.Game.Screens.Select
new WedgeBackground
{
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = carousel_width * 0.76f },
+ Padding = new MarginPadding { Right = -150 },
+ Size = new Vector2(wedged_container_size.X, 1),
}
}
},
@@ -144,8 +144,8 @@ namespace osu.Game.Screens.Select
Carousel = new BeatmapCarousel
{
Masking = false,
- RelativeSizeAxes = Axes.Y,
- Size = new Vector2(carousel_width, 1),
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(1 - wedged_container_size.X, 1),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
SelectionChanged = updateSelectedBeatmap,
@@ -223,9 +223,9 @@ namespace osu.Game.Screens.Select
if (Footer != null)
{
- Footer.AddButton(@"mods", colours.Yellow, ModSelect, Key.F1);
- Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
- Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
+ Footer.AddButton(new FooterButtonMods(mods), ModSelect);
+ Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
+ Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1);
@@ -505,6 +505,8 @@ namespace osu.Game.Screens.Select
{
ModSelect.Hide();
+ BeatmapOptions.Hide();
+
this.ScaleTo(1.1f, 250, Easing.InSine);
this.FadeOut(250);
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
index 1182cacfc1..2b27a56844 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Threading;
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -57,7 +58,7 @@ namespace osu.Game.Storyboards.Drawables
}
[BackgroundDependencyLoader(true)]
- private void load(FileStore fileStore, GameplayClock clock)
+ private void load(FileStore fileStore, GameplayClock clock, CancellationToken? cancellationToken)
{
if (clock != null)
Clock = clock;
@@ -65,7 +66,11 @@ namespace osu.Game.Storyboards.Drawables
dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1));
foreach (var layer in Storyboard.Layers)
+ {
+ cancellationToken?.ThrowIfCancellationRequested();
+
Add(layer.CreateDrawable());
+ }
}
private void updateLayerVisibility()
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
index 106ebfaf5d..fd2d441f34 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -24,10 +25,12 @@ namespace osu.Game.Storyboards.Drawables
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(CancellationToken? cancellationToken)
{
foreach (var element in Layer.Elements)
{
+ cancellationToken?.ThrowIfCancellationRequested();
+
if (element.IsDrawable)
AddInternal(element.CreateDrawable());
}
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
index 0cc753ff7e..3d988c5fe3 100644
--- a/osu.Game/Storyboards/Storyboard.cs
+++ b/osu.Game/Storyboards/Storyboard.cs
@@ -5,11 +5,10 @@ using osu.Game.Beatmaps;
using osu.Game.Storyboards.Drawables;
using System.Collections.Generic;
using System.Linq;
-using System;
namespace osu.Game.Storyboards
{
- public class Storyboard : IDisposable
+ public class Storyboard
{
private readonly Dictionary layers = new Dictionary();
public IEnumerable Layers => layers.Values;
@@ -56,30 +55,5 @@ namespace osu.Game.Storyboards
drawable.Width = drawable.Height * (BeatmapInfo.WidescreenStoryboard ? 16 / 9f : 4 / 3f);
return drawable;
}
-
- #region Disposal
-
- ~Storyboard()
- {
- Dispose(false);
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private bool isDisposed;
-
- protected virtual void Dispose(bool isDisposing)
- {
- if (isDisposed)
- return;
-
- isDisposed = true;
- }
-
- #endregion
}
}
diff --git a/osu.Game/Tests/Visual/AllPlayersTestCase.cs b/osu.Game/Tests/Visual/AllPlayersTestCase.cs
index 6e78851e31..dc3ef1a85b 100644
--- a/osu.Game/Tests/Visual/AllPlayersTestCase.cs
+++ b/osu.Game/Tests/Visual/AllPlayersTestCase.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
@@ -41,6 +42,10 @@ namespace osu.Game.Tests.Visual
AddCheckSteps();
}
+
+ OsuConfigManager manager;
+ Dependencies.Cache(manager = new OsuConfigManager(LocalStorage));
+ manager.GetBindable(OsuSetting.DimLevel).Value = 1.0;
}
protected abstract void AddCheckSteps();
@@ -71,6 +76,6 @@ namespace osu.Game.Tests.Visual
return Player;
}
- protected virtual Player CreatePlayer(Ruleset ruleset) => new Player(false, false);
+ protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false);
}
}
diff --git a/osu.Game/Tests/Visual/PlayerTestCase.cs b/osu.Game/Tests/Visual/PlayerTestCase.cs
index b9c7933cfb..5e453f0ac8 100644
--- a/osu.Game/Tests/Visual/PlayerTestCase.cs
+++ b/osu.Game/Tests/Visual/PlayerTestCase.cs
@@ -1,9 +1,11 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// 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.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
@@ -22,8 +24,16 @@ namespace osu.Game.Tests.Visual
this.ruleset = ruleset;
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ OsuConfigManager manager;
+ Dependencies.Cache(manager = new OsuConfigManager(LocalStorage));
+ manager.GetBindable(OsuSetting.DimLevel).Value = 1.0;
+ }
+
[SetUpSteps]
- public void SetUpSteps()
+ public virtual void SetUpSteps()
{
AddStep(ruleset.RulesetInfo.Name, loadPlayer);
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
@@ -46,6 +56,6 @@ namespace osu.Game.Tests.Visual
LoadScreen(Player);
}
- protected virtual Player CreatePlayer(Ruleset ruleset) => new Player(false, false);
+ protected virtual Player CreatePlayer(Ruleset ruleset) => new TestPlayer(false, false);
}
}
diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs
new file mode 100644
index 0000000000..b93a1466e0
--- /dev/null
+++ b/osu.Game/Tests/Visual/TestPlayer.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestPlayer : Player
+ {
+ protected override bool PauseOnFocusLost => false;
+
+ public TestPlayer(bool allowPause = true, bool showResults = true)
+ : base(allowPause, showResults)
+ {
+ }
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b5f6be32ed..8f733a5c55 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -11,11 +11,11 @@
-
-
-
-
-
+
+
+
+
+