diff --git a/build.ps1 b/build.ps1
index 8eb37f2de6..2dbd10a150 100755
--- a/build.ps1
+++ b/build.ps1
@@ -1,4 +1,27 @@
+[CmdletBinding()]
+Param(
+ [string]$Target,
+ [string]$Configuration,
+ [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
+ [string]$Verbosity,
+ [switch]$ShowDescription,
+ [Alias("WhatIf", "Noop")]
+ [switch]$DryRun,
+ [Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
+ [string[]]$ScriptArgs
+)
+
+# Build Cake arguments
+$cakeArguments = "";
+if ($Target) { $cakeArguments += "-target=$Target" }
+if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
+if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
+if ($ShowDescription) { $cakeArguments += "-showdescription" }
+if ($DryRun) { $cakeArguments += "-dryrun" }
+if ($Experimental) { $cakeArguments += "-experimental" }
+$cakeArguments += $ScriptArgs
+
dotnet tool install Cake.Tool --global --version 0.35.0
dotnet cake ./build/build.cake --bootstrap
-dotnet cake ./build/build.cake
+dotnet cake ./build/build.cake $cakeArguments
exit $LASTEXITCODE
\ No newline at end of file
diff --git a/build.sh b/build.sh
index d20a9c12fa..ac6bd877a6 100755
--- a/build.sh
+++ b/build.sh
@@ -1,3 +1,17 @@
+echo "Installing Cake.Tool..."
dotnet tool install Cake.Tool --global --version 0.35.0
+
+# Parse arguments.
+CAKE_ARGUMENTS=()
+for i in "$@"; do
+ case $1 in
+ -s|--script) SCRIPT="$2"; shift ;;
+ --) shift; CAKE_ARGUMENTS+=("$@"); break ;;
+ *) CAKE_ARGUMENTS+=("$1") ;;
+ esac
+ shift
+done
+
+echo "Running build script..."
dotnet cake ./build/build.cake --bootstrap
-dotnet cake ./build/build.cake
\ No newline at end of file
+dotnet cake ./build/build.cake "${CAKE_ARGUMENTS[@]}"
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
index d3c12b1944..cc50459a0c 100644
--- a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; }
- protected new DrawableManiaHitObject HitObject => (DrawableManiaHitObject)base.HitObject;
+ public new DrawableManiaHitObject HitObject => (DrawableManiaHitObject)base.HitObject;
protected IClock EditorClock { get; private set; }
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
index 6f49c7f0c4..f576c43e52 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaSelectionHandler.cs
@@ -3,9 +3,7 @@
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Input.Events;
using osu.Framework.Timing;
-using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
@@ -31,13 +29,16 @@ namespace osu.Game.Rulesets.Mania.Edit
editorClock = clock;
}
- public override void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
+ public override void HandleMovement(MoveSelectionEvent moveEvent)
{
- adjustOrigins((ManiaSelectionBlueprint)blueprint);
- performDragMovement(dragEvent);
- performColumnMovement(dragEvent);
+ var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
+ int lastColumn = maniaBlueprint.HitObject.HitObject.Column;
- base.HandleDrag(blueprint, dragEvent);
+ adjustOrigins(maniaBlueprint);
+ performDragMovement(moveEvent);
+ performColumnMovement(lastColumn, moveEvent);
+
+ base.HandleMovement(moveEvent);
}
///
@@ -62,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Edit
b.HitObject.Y += movementDelta;
}
- private void performDragMovement(DragEvent dragEvent)
+ private void performDragMovement(MoveSelectionEvent moveEvent)
{
foreach (var b in SelectedBlueprints)
{
@@ -72,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Edit
// Using the hitobject position is required since AdjustPosition can be invoked multiple times per frame
// without the position having been updated by the parenting ScrollingHitObjectContainer
- hitObject.Y += dragEvent.Delta.Y;
+ hitObject.Y += moveEvent.InstantDelta.Y;
float targetPosition;
@@ -94,14 +95,13 @@ namespace osu.Game.Rulesets.Mania.Edit
}
}
- private void performColumnMovement(DragEvent dragEvent)
+ private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
{
- var lastColumn = composer.ColumnAt(dragEvent.ScreenSpaceLastMousePosition);
- var currentColumn = composer.ColumnAt(dragEvent.ScreenSpaceMousePosition);
- if (lastColumn == null || currentColumn == null)
+ var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition);
+ if (currentColumn == null)
return;
- int columnDelta = currentColumn.Index - lastColumn.Index;
+ int columnDelta = currentColumn.Index - lastColumn;
if (columnDelta == 0)
return;
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
index 1ab1219ab0..472267eb66 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs
@@ -2,8 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
-using osu.Framework.Input.Events;
-using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
@@ -11,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuSelectionHandler : SelectionHandler
{
- public override void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
+ public override void HandleMovement(MoveSelectionEvent moveEvent)
{
foreach (var h in SelectedHitObjects.OfType())
{
@@ -21,10 +19,10 @@ namespace osu.Game.Rulesets.Osu.Edit
continue;
}
- h.Position += dragEvent.Delta;
+ h.Position += moveEvent.InstantDelta;
}
- base.HandleDrag(blueprint, dragEvent);
+ base.HandleMovement(moveEvent);
}
}
}
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index e96637cae9..4e81954f50 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -510,9 +510,9 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
- public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null)
+ public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
- var temp = path ?? TestResources.GetTestBeatmapForImport();
+ var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
var manager = osu.Dependencies.Get();
diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs
index 9cb85a63bf..66084a3204 100644
--- a/osu.Game.Tests/Resources/TestResources.cs
+++ b/osu.Game.Tests/Resources/TestResources.cs
@@ -11,13 +11,13 @@ namespace osu.Game.Tests.Resources
{
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
- public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz");
+ public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
- public static string GetTestBeatmapForImport()
+ public static string GetTestBeatmapForImport(bool virtualTrack = false)
{
var temp = Path.GetTempFileName() + ".osz";
- using (var stream = GetTestBeatmapStream())
+ using (var stream = GetTestBeatmapStream(virtualTrack))
using (var newFile = File.Create(temp))
stream.CopyTo(newFile);
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs
index 17535cae98..471f67b7b6 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneScreenNavigation.cs
@@ -5,12 +5,15 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio.Track;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
+using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
@@ -18,7 +21,9 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
+using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
+using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@@ -31,11 +36,11 @@ namespace osu.Game.Tests.Visual.Menus
private const float click_padding = 25;
private GameHost host;
- private TestOsuGame osuGame;
+ private TestOsuGame game;
- private Vector2 backButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, osuGame.LayoutRectangle.Bottom - click_padding));
+ private Vector2 backButtonPosition => game.ToScreenSpace(new Vector2(click_padding, game.LayoutRectangle.Bottom - click_padding));
- private Vector2 optionsButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, click_padding));
+ private Vector2 optionsButtonPosition => game.ToScreenSpace(new Vector2(click_padding, click_padding));
[BackgroundDependencyLoader]
private void load(GameHost host)
@@ -54,23 +59,23 @@ namespace osu.Game.Tests.Visual.Menus
{
AddStep("Create new game instance", () =>
{
- if (osuGame != null)
+ if (game != null)
{
- Remove(osuGame);
- osuGame.Dispose();
+ Remove(game);
+ game.Dispose();
}
- osuGame = new TestOsuGame(LocalStorage, API);
- osuGame.SetHost(host);
+ game = new TestOsuGame(LocalStorage, API);
+ game.SetHost(host);
// todo: this can be removed once we can run audio trakcs without a device present
// see https://github.com/ppy/osu/issues/1302
- osuGame.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
+ game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
- Add(osuGame);
+ Add(game);
});
- AddUntilStep("Wait for load", () => osuGame.IsLoaded);
- AddUntilStep("Wait for intro", () => osuGame.ScreenStack.CurrentScreen is IntroScreen);
+ AddUntilStep("Wait for load", () => game.IsLoaded);
+ AddUntilStep("Wait for intro", () => game.ScreenStack.CurrentScreen is IntroScreen);
confirmAtMainMenu();
}
@@ -82,11 +87,43 @@ namespace osu.Game.Tests.Visual.Menus
pushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
- AddStep("Press escape", () => pressAndRelease(Key.Escape));
+ pushEscape();
AddAssert("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
exitViaEscapeAndConfirm();
}
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TestSongContinuesAfterExitPlayer(bool withUserPause)
+ {
+ Player player = null;
+
+ WorkingBeatmap beatmap() => game.Beatmap.Value;
+ Track track() => beatmap().Track;
+
+ pushAndConfirm(() => new TestSongSelect());
+
+ AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Wait());
+
+ AddUntilStep("wait for selected", () => !game.Beatmap.IsDefault);
+
+ if (withUserPause)
+ AddStep("pause", () => game.Dependencies.Get().Stop());
+
+ AddStep("press enter", () => pressAndRelease(Key.Enter));
+
+ AddUntilStep("wait for player", () => (player = game.ScreenStack.CurrentScreen as Player) != null);
+ AddUntilStep("wait for fail", () => player.HasFailed);
+
+ AddUntilStep("wait for track stop", () => !track().IsRunning);
+ AddAssert("Ensure time before preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
+
+ pushEscape();
+
+ AddUntilStep("wait for track playing", () => track().IsRunning);
+ AddAssert("Ensure time wasn't reset to preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
+ }
+
[Test]
public void TestExitSongSelectWithClick()
{
@@ -98,7 +135,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered.
- AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == osuGame.BackButton));
+ AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == game.BackButton));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@@ -122,25 +159,28 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestOpenOptionsAndExitWithEscape()
{
- AddUntilStep("Wait for options to load", () => osuGame.Settings.IsLoaded);
+ AddUntilStep("Wait for options to load", () => game.Settings.IsLoaded);
AddStep("Enter menu", () => pressAndRelease(Key.Enter));
AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left));
- AddAssert("Options overlay was opened", () => osuGame.Settings.State.Value == Visibility.Visible);
+ AddAssert("Options overlay was opened", () => game.Settings.State.Value == Visibility.Visible);
AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape));
- AddAssert("Options overlay was closed", () => osuGame.Settings.State.Value == Visibility.Hidden);
+ AddAssert("Options overlay was closed", () => game.Settings.State.Value == Visibility.Hidden);
}
private void pushAndConfirm(Func newScreen)
{
Screen screen = null;
- AddStep("Push new screen", () => osuGame.ScreenStack.Push(screen = newScreen()));
- AddUntilStep("Wait for new screen", () => osuGame.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
+ AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen()));
+ AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
}
+ private void pushEscape() =>
+ AddStep("Press escape", () => pressAndRelease(Key.Escape));
+
private void exitViaEscapeAndConfirm()
{
- AddStep("Press escape", () => pressAndRelease(Key.Escape));
+ pushEscape();
confirmAtMainMenu();
}
@@ -151,7 +191,7 @@ namespace osu.Game.Tests.Visual.Menus
confirmAtMainMenu();
}
- private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => osuGame.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
+ private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void pressAndRelease(Key key)
{
@@ -169,6 +209,8 @@ namespace osu.Game.Tests.Visual.Menus
public new OsuConfigManager LocalConfig => base.LocalConfig;
+ public new Bindable Beatmap => base.Beatmap;
+
protected override Loader CreateLoader() => new TestLoader();
public TestOsuGame(Storage storage, IAPIProvider api)
diff --git a/osu.Game/Online/API/Requests/GetUsersRequest.cs b/osu.Game/Online/API/Requests/GetUsersRequest.cs
index 55df88b7e5..b75ecd5bd7 100644
--- a/osu.Game/Online/API/Requests/GetUsersRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUsersRequest.cs
@@ -1,12 +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 System.Collections.Generic;
-using osu.Game.Online.API.Requests.Responses;
-
namespace osu.Game.Online.API.Requests
{
- public class GetUsersRequest : APIRequest>
+ public class GetUsersRequest : APIRequest
{
protected override string Target => @"rankings/osu/performance";
}
diff --git a/osu.Game/Online/API/Requests/GetUsersResponse.cs b/osu.Game/Online/API/Requests/GetUsersResponse.cs
new file mode 100644
index 0000000000..860785875a
--- /dev/null
+++ b/osu.Game/Online/API/Requests/GetUsersResponse.cs
@@ -0,0 +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.Collections.Generic;
+using Newtonsoft.Json;
+using osu.Game.Online.API.Requests.Responses;
+
+namespace osu.Game.Online.API.Requests
+{
+ public class GetUsersResponse : ResponseWithCursor
+ {
+ [JsonProperty("ranking")]
+ public List Users;
+ }
+}
diff --git a/osu.Game/Online/API/Requests/ResponseWithCursor.cs b/osu.Game/Online/API/Requests/ResponseWithCursor.cs
new file mode 100644
index 0000000000..e38e73dd01
--- /dev/null
+++ b/osu.Game/Online/API/Requests/ResponseWithCursor.cs
@@ -0,0 +1,16 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Online.API.Requests
+{
+ public abstract class ResponseWithCursor
+ {
+ ///
+ /// A collection of parameters which should be passed to the search endpoint to fetch the next page.
+ ///
+ [JsonProperty("cursor")]
+ public dynamic CursorJson;
+ }
+}
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
index b0f4fef81a..28863cb0e0 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
@@ -2,19 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
-using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
- public class SearchBeatmapSetsResponse
+ public class SearchBeatmapSetsResponse : ResponseWithCursor
{
public IEnumerable BeatmapSets;
-
- ///
- /// A collection of parameters which should be passed to the search endpoint to fetch the next page.
- ///
- [JsonProperty("cursor")]
- public dynamic CursorJson;
}
}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index f5c36a9cac..9ec0364420 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -98,20 +98,13 @@ namespace osu.Game.Overlays
///
/// Start playing the current track (if not already playing).
///
- public void Play()
- {
- if (!IsPlaying)
- TogglePause();
- }
-
- ///
- /// Toggle pause / play.
- ///
/// Whether the operation was successful.
- public bool TogglePause()
+ public bool Play(bool restart = false)
{
var track = current?.Track;
+ IsUserPaused = false;
+
if (track == null)
{
if (beatmap.Disabled)
@@ -121,16 +114,38 @@ namespace osu.Game.Overlays
return true;
}
- if (track.IsRunning)
- {
- IsUserPaused = true;
- track.Stop();
- }
- else
- {
+ if (restart)
+ track.Restart();
+ else if (!IsPlaying)
track.Start();
- IsUserPaused = false;
- }
+
+ return true;
+ }
+
+ ///
+ /// Stop playing the current track and pause at the current position.
+ ///
+ public void Stop()
+ {
+ var track = current?.Track;
+
+ IsUserPaused = true;
+ if (track?.IsRunning == true)
+ track.Stop();
+ }
+
+ ///
+ /// Toggle pause / play.
+ ///
+ /// Whether the operation was successful.
+ public bool TogglePause()
+ {
+ var track = current?.Track;
+
+ if (track?.IsRunning == true)
+ Stop();
+ else
+ Play();
return true;
}
diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs
index 4def249200..6f468bbeb7 100644
--- a/osu.Game/Overlays/SocialOverlay.cs
+++ b/osu.Game/Overlays/SocialOverlay.cs
@@ -118,7 +118,7 @@ namespace osu.Game.Overlays
default:
var userRequest = new GetUsersRequest(); // TODO filter arguments!
- userRequest.Success += response => updateUsers(response.Select(r => r.User));
+ userRequest.Success += res => updateUsers(res.Users.Select(r => r.User));
API.Queue(getUsersRequest = userRequest);
break;
}
diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
index 0f77b8d584..838984b223 100644
--- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
@@ -45,6 +45,11 @@ namespace osu.Game.Rulesets.Edit
///
public readonly DrawableHitObject HitObject;
+ ///
+ /// The screen-space position of prior to handling a movement event.
+ ///
+ internal Vector2 ScreenSpaceMovementStartPosition { get; private set; }
+
protected override bool ShouldBeAlive => (HitObject.IsAlive && HitObject.IsPresent) || State == SelectionState.Selected;
public override bool HandlePositionalInput => ShouldBeAlive;
public override bool RemoveWhenNotAlive => false;
@@ -131,7 +136,11 @@ namespace osu.Game.Rulesets.Edit
return base.OnClick(e);
}
- protected override bool OnDragStart(DragStartEvent e) => true;
+ protected override bool OnDragStart(DragStartEvent e)
+ {
+ ScreenSpaceMovementStartPosition = HitObject.ToScreenSpace(HitObject.OriginPosition);
+ return true;
+ }
protected override bool OnDrag(DragEvent e)
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index d96d88c2b9..2de5ecf633 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -216,7 +216,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state);
- private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent);
+ private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent)
+ {
+ var movePosition = blueprint.ScreenSpaceMovementStartPosition + dragEvent.ScreenSpaceMousePosition - dragEvent.ScreenSpaceMouseDownPosition;
+
+ selectionHandler.HandleMovement(new MoveSelectionEvent(blueprint, blueprint.ScreenSpaceMovementStartPosition, movePosition));
+ }
protected override void Dispose(bool isDisposing)
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs
new file mode 100644
index 0000000000..13945381bb
--- /dev/null
+++ b/osu.Game/Screens/Edit/Compose/Components/MoveSelectionEvent.cs
@@ -0,0 +1,46 @@
+// 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.Edit;
+using osuTK;
+
+namespace osu.Game.Screens.Edit.Compose.Components
+{
+ ///
+ /// An event which occurs when a is moved.
+ ///
+ public class MoveSelectionEvent
+ {
+ ///
+ /// The that triggered this .
+ ///
+ public readonly SelectionBlueprint Blueprint;
+
+ ///
+ /// The starting screen-space position of the hitobject.
+ ///
+ public readonly Vector2 ScreenSpaceStartPosition;
+
+ ///
+ /// The expected screen-space position of the hitobject at the current cursor position.
+ ///
+ public readonly Vector2 ScreenSpacePosition;
+
+ ///
+ /// The distance between and the hitobject's current position, in the coordinate-space of the hitobject's parent.
+ ///
+ ///
+ /// This does not use and does not represent the cumulative movement distance.
+ ///
+ public readonly Vector2 InstantDelta;
+
+ public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition)
+ {
+ Blueprint = blueprint;
+ ScreenSpaceStartPosition = screenSpaceStartPosition;
+ ScreenSpacePosition = screenSpacePosition;
+
+ InstantDelta = Blueprint.HitObject.Parent.ToLocalSpace(ScreenSpacePosition) - Blueprint.HitObject.Position;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
index 11e649168f..c9e862d99e 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
@@ -65,11 +65,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region User Input Handling
///
- /// Handles the selected s being dragged.
+ /// Handles the selected s being moved.
///
- /// The that received the drag event.
- /// The drag event.
- public virtual void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
+ /// The move event.
+ public virtual void HandleMovement(MoveSelectionEvent moveEvent)
{
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 055ad89b6d..409ea4bbbe 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -486,7 +486,9 @@ namespace osu.Game.Screens.Select
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
{
UpdateBeatmap(Beatmap.Value);
- ensurePlayingSelected();
+
+ // restart playback on returning to song select, regardless.
+ music?.Play();
}
base.OnResuming(last);
@@ -592,7 +594,7 @@ namespace osu.Game.Screens.Select
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
if (!track.IsRunning && (music?.IsUserPaused != true || isNewTrack))
- track.Restart();
+ music?.Play(true);
lastTrack.SetTarget(track);
}