mirror of
https://github.com/ppy/osu.git
synced 2025-03-13 15:07:45 +08:00
Merge branch 'master' into editorfix
This commit is contained in:
commit
37b1ad2392
@ -79,5 +79,114 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
trackChangeQueue.Peek().changeDirection == TrackChangeDirection.Next);
|
trackChangeQueue.Peek().changeDirection == TrackChangeDirection.Next);
|
||||||
AddAssert("track actually changed", () => !trackChangeQueue.First().working.BeatmapInfo.Equals(trackChangeQueue.Last().working.BeatmapInfo));
|
AddAssert("track actually changed", () => !trackChangeQueue.First().working.BeatmapInfo.Equals(trackChangeQueue.Last().working.BeatmapInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShuffleBackwards()
|
||||||
|
{
|
||||||
|
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null!;
|
||||||
|
|
||||||
|
AddStep("enable shuffle", () => Game.MusicController.Shuffle.Value = true);
|
||||||
|
|
||||||
|
// ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
|
||||||
|
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()), 5);
|
||||||
|
AddStep("ensure nonzero track duration", () => Game.Realm.Write(r =>
|
||||||
|
{
|
||||||
|
// this was already supposed to be non-zero (see innards of `TestResources.CreateTestBeatmapSetInfo()`),
|
||||||
|
// but the non-zero value is being overwritten *to* zero by `BeatmapUpdater`.
|
||||||
|
// do a bit of a hack to change it back again - otherwise tracks are going to switch instantly and we won't be able to assert anything sane anymore.
|
||||||
|
foreach (var beatmap in r.All<BeatmapInfo>().Where(b => b.Length == 0))
|
||||||
|
beatmap.Length = 60_000;
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("bind to track change", () =>
|
||||||
|
{
|
||||||
|
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
|
||||||
|
Game.MusicController.TrackChanged += (working, changeDirection) => trackChangeQueue.Enqueue((working, changeDirection));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("seek track to 6 second", () => Game.MusicController.SeekTo(6000));
|
||||||
|
AddUntilStep("wait for current time to update", () => Game.MusicController.CurrentTrack.CurrentTime > 5000);
|
||||||
|
|
||||||
|
AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
|
||||||
|
AddAssert("no track change", () => trackChangeQueue.Count == 0);
|
||||||
|
AddUntilStep("track restarted", () => Game.MusicController.CurrentTrack.CurrentTime < 5000);
|
||||||
|
|
||||||
|
AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
|
||||||
|
AddUntilStep("track changed", () => trackChangeQueue.Count == 1);
|
||||||
|
|
||||||
|
AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
|
||||||
|
AddUntilStep("track changed", () => trackChangeQueue.Count == 2);
|
||||||
|
|
||||||
|
AddStep("press next", () => globalActionContainer.TriggerPressed(GlobalAction.MusicNext));
|
||||||
|
AddUntilStep("track changed", () =>
|
||||||
|
trackChangeQueue.Count == 3 && !trackChangeQueue.ElementAt(1).working.BeatmapInfo.Equals(trackChangeQueue.Last().working.BeatmapInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShuffleForwards()
|
||||||
|
{
|
||||||
|
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null!;
|
||||||
|
|
||||||
|
AddStep("enable shuffle", () => Game.MusicController.Shuffle.Value = true);
|
||||||
|
|
||||||
|
// ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
|
||||||
|
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()), 5);
|
||||||
|
AddStep("ensure nonzero track duration", () => Game.Realm.Write(r =>
|
||||||
|
{
|
||||||
|
// this was already supposed to be non-zero (see innards of `TestResources.CreateTestBeatmapSetInfo()`),
|
||||||
|
// but the non-zero value is being overwritten *to* zero by `BeatmapUpdater`.
|
||||||
|
// do a bit of a hack to change it back again - otherwise tracks are going to switch instantly and we won't be able to assert anything sane anymore.
|
||||||
|
foreach (var beatmap in r.All<BeatmapInfo>().Where(b => b.Length == 0))
|
||||||
|
beatmap.Length = 60_000;
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("bind to track change", () =>
|
||||||
|
{
|
||||||
|
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
|
||||||
|
Game.MusicController.TrackChanged += (working, changeDirection) => trackChangeQueue.Enqueue((working, changeDirection));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("press next", () => globalActionContainer.TriggerPressed(GlobalAction.MusicNext));
|
||||||
|
AddUntilStep("track changed", () => trackChangeQueue.Count == 1);
|
||||||
|
|
||||||
|
AddStep("press next", () => globalActionContainer.TriggerPressed(GlobalAction.MusicNext));
|
||||||
|
AddUntilStep("track changed", () => trackChangeQueue.Count == 2);
|
||||||
|
|
||||||
|
AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
|
||||||
|
AddUntilStep("track changed", () =>
|
||||||
|
trackChangeQueue.Count == 3 && !trackChangeQueue.ElementAt(1).working.BeatmapInfo.Equals(trackChangeQueue.Last().working.BeatmapInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShuffleBackAndForth()
|
||||||
|
{
|
||||||
|
Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null!;
|
||||||
|
|
||||||
|
AddStep("enable shuffle", () => Game.MusicController.Shuffle.Value = true);
|
||||||
|
|
||||||
|
// ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
|
||||||
|
AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(TestResources.CreateTestBeatmapSetInfo()), 5);
|
||||||
|
AddStep("ensure nonzero track duration", () => Game.Realm.Write(r =>
|
||||||
|
{
|
||||||
|
// this was already supposed to be non-zero (see innards of `TestResources.CreateTestBeatmapSetInfo()`),
|
||||||
|
// but the non-zero value is being overwritten *to* zero by `BeatmapUpdater`.
|
||||||
|
// do a bit of a hack to change it back again - otherwise tracks are going to switch instantly and we won't be able to assert anything sane anymore.
|
||||||
|
foreach (var beatmap in r.All<BeatmapInfo>().Where(b => b.Length == 0))
|
||||||
|
beatmap.Length = 60_000;
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("bind to track change", () =>
|
||||||
|
{
|
||||||
|
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
|
||||||
|
Game.MusicController.TrackChanged += (working, changeDirection) => trackChangeQueue.Enqueue((working, changeDirection));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("press next", () => globalActionContainer.TriggerPressed(GlobalAction.MusicNext));
|
||||||
|
AddUntilStep("track changed", () => trackChangeQueue.Count == 1);
|
||||||
|
|
||||||
|
AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
|
||||||
|
AddUntilStep("track changed", () =>
|
||||||
|
trackChangeQueue.Count == 2 && !trackChangeQueue.First().working.BeatmapInfo.Equals(trackChangeQueue.Last().working.BeatmapInfo));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ namespace osu.Game.Overlays
|
|||||||
private readonly Bindable<RandomSelectAlgorithm> randomSelectAlgorithm = new Bindable<RandomSelectAlgorithm>();
|
private readonly Bindable<RandomSelectAlgorithm> randomSelectAlgorithm = new Bindable<RandomSelectAlgorithm>();
|
||||||
private readonly List<Live<BeatmapSetInfo>> previousRandomSets = new List<Live<BeatmapSetInfo>>();
|
private readonly List<Live<BeatmapSetInfo>> previousRandomSets = new List<Live<BeatmapSetInfo>>();
|
||||||
private int randomHistoryDirection;
|
private int randomHistoryDirection;
|
||||||
|
private int lastRandomTrackDirection;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, OsuConfigManager configManager)
|
private void load(AudioManager audio, OsuConfigManager configManager)
|
||||||
@ -371,54 +372,72 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private Live<BeatmapSetInfo>? getNextRandom(int direction, bool allowProtectedTracks)
|
private Live<BeatmapSetInfo>? getNextRandom(int direction, bool allowProtectedTracks)
|
||||||
{
|
{
|
||||||
Live<BeatmapSetInfo> result;
|
try
|
||||||
|
|
||||||
var possibleSets = getBeatmapSets().Where(s => !s.Value.Protected || allowProtectedTracks).ToArray();
|
|
||||||
|
|
||||||
if (possibleSets.Length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// condition below checks if the signs of `randomHistoryDirection` and `direction` are opposite and not zero.
|
|
||||||
// if that is the case, it means that the user had previously chosen next track `randomHistoryDirection` times and wants to go back,
|
|
||||||
// or that the user had previously chosen previous track `randomHistoryDirection` times and wants to go forward.
|
|
||||||
// in both cases, it means that we have a history of previous random selections that we can rewind.
|
|
||||||
if (randomHistoryDirection * direction < 0)
|
|
||||||
{
|
{
|
||||||
Debug.Assert(Math.Abs(randomHistoryDirection) == previousRandomSets.Count);
|
Live<BeatmapSetInfo> result;
|
||||||
result = previousRandomSets[^1];
|
|
||||||
previousRandomSets.RemoveAt(previousRandomSets.Count - 1);
|
|
||||||
randomHistoryDirection += direction;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the early-return above didn't cover it, it means that we have no history to fall back on
|
var possibleSets = getBeatmapSets().Where(s => !s.Value.Protected || allowProtectedTracks).ToArray();
|
||||||
// and need to actually choose something random.
|
|
||||||
switch (randomSelectAlgorithm.Value)
|
|
||||||
{
|
|
||||||
case RandomSelectAlgorithm.Random:
|
|
||||||
result = possibleSets[RNG.Next(possibleSets.Length)];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RandomSelectAlgorithm.RandomPermutation:
|
if (possibleSets.Length == 0)
|
||||||
var notYetPlayedSets = possibleSets.Except(previousRandomSets).ToArray();
|
return null;
|
||||||
|
|
||||||
if (notYetPlayedSets.Length == 0)
|
// condition below checks if the signs of `randomHistoryDirection` and `direction` are opposite and not zero.
|
||||||
|
// if that is the case, it means that the user had previously chosen next track `randomHistoryDirection` times and wants to go back,
|
||||||
|
// or that the user had previously chosen previous track `randomHistoryDirection` times and wants to go forward.
|
||||||
|
// in both cases, it means that we have a history of previous random selections that we can rewind.
|
||||||
|
if (randomHistoryDirection * direction < 0)
|
||||||
|
{
|
||||||
|
Debug.Assert(Math.Abs(randomHistoryDirection) == previousRandomSets.Count);
|
||||||
|
|
||||||
|
// if the user has been shuffling backwards and now going forwards (or vice versa),
|
||||||
|
// the topmost item from history needs to be discarded because it's the *current* track.
|
||||||
|
if (direction * lastRandomTrackDirection < 0)
|
||||||
{
|
{
|
||||||
notYetPlayedSets = possibleSets;
|
previousRandomSets.RemoveAt(previousRandomSets.Count - 1);
|
||||||
previousRandomSets.Clear();
|
randomHistoryDirection += direction;
|
||||||
randomHistoryDirection = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = notYetPlayedSets[RNG.Next(notYetPlayedSets.Length)];
|
if (previousRandomSets.Count > 0)
|
||||||
break;
|
{
|
||||||
|
result = previousRandomSets[^1];
|
||||||
|
previousRandomSets.RemoveAt(previousRandomSets.Count - 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
// if the early-return above didn't cover it, it means that we have no history to fall back on
|
||||||
throw new ArgumentOutOfRangeException(nameof(randomSelectAlgorithm), randomSelectAlgorithm.Value, "Unsupported random select algorithm");
|
// and need to actually choose something random.
|
||||||
|
switch (randomSelectAlgorithm.Value)
|
||||||
|
{
|
||||||
|
case RandomSelectAlgorithm.Random:
|
||||||
|
result = possibleSets[RNG.Next(possibleSets.Length)];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RandomSelectAlgorithm.RandomPermutation:
|
||||||
|
var notYetPlayedSets = possibleSets.Except(previousRandomSets).ToArray();
|
||||||
|
|
||||||
|
if (notYetPlayedSets.Length == 0)
|
||||||
|
{
|
||||||
|
notYetPlayedSets = possibleSets;
|
||||||
|
previousRandomSets.Clear();
|
||||||
|
randomHistoryDirection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = notYetPlayedSets[RNG.Next(notYetPlayedSets.Length)];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(randomSelectAlgorithm), randomSelectAlgorithm.Value, "Unsupported random select algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
previousRandomSets.Add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
randomHistoryDirection += direction;
|
||||||
|
lastRandomTrackDirection = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
previousRandomSets.Add(result);
|
|
||||||
randomHistoryDirection += direction;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restartTrack()
|
private void restartTrack()
|
||||||
|
@ -150,13 +150,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
switch (e.Key)
|
switch (e.Key)
|
||||||
{
|
{
|
||||||
case Key.G:
|
case Key.G:
|
||||||
return CanReverse && reverseButton?.TriggerClick() == true;
|
if (!CanReverse || reverseButton == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
reverseButton.TriggerAction();
|
||||||
|
return true;
|
||||||
|
|
||||||
case Key.Comma:
|
case Key.Comma:
|
||||||
return canRotate.Value && rotateCounterClockwiseButton?.TriggerClick() == true;
|
if (!canRotate.Value || rotateCounterClockwiseButton == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rotateCounterClockwiseButton.TriggerAction();
|
||||||
|
return true;
|
||||||
|
|
||||||
case Key.Period:
|
case Key.Period:
|
||||||
return canRotate.Value && rotateClockwiseButton?.TriggerClick() == true;
|
if (!canRotate.Value || rotateClockwiseButton == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rotateClockwiseButton.TriggerAction();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnKeyDown(e);
|
return base.OnKeyDown(e);
|
||||||
@ -285,7 +297,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
Action = action
|
Action = action
|
||||||
};
|
};
|
||||||
|
|
||||||
button.OperationStarted += freezeButtonPosition;
|
button.Clicked += freezeButtonPosition;
|
||||||
button.HoverLost += unfreezeButtonPosition;
|
button.HoverLost += unfreezeButtonPosition;
|
||||||
|
|
||||||
button.OperationStarted += operationStarted;
|
button.OperationStarted += operationStarted;
|
||||||
|
@ -21,6 +21,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
public Action? Action;
|
public Action? Action;
|
||||||
|
|
||||||
|
public event Action? Clicked;
|
||||||
|
|
||||||
public event Action? HoverLost;
|
public event Action? HoverLost;
|
||||||
|
|
||||||
public SelectionBoxButton(IconUsage iconUsage, string tooltip)
|
public SelectionBoxButton(IconUsage iconUsage, string tooltip)
|
||||||
@ -49,11 +51,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
Circle.FlashColour(Colours.GrayF, 300);
|
Clicked?.Invoke();
|
||||||
|
|
||||||
|
TriggerAction();
|
||||||
|
|
||||||
TriggerOperationStarted();
|
|
||||||
Action?.Invoke();
|
|
||||||
TriggerOperationEnded();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,5 +72,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LocalisableString TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
|
public void TriggerAction()
|
||||||
|
{
|
||||||
|
Circle.FlashColour(Colours.GrayF, 300);
|
||||||
|
|
||||||
|
TriggerOperationStarted();
|
||||||
|
Action?.Invoke();
|
||||||
|
TriggerOperationEnded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user