Fix switching direction when changing tracks with shuffle on restarting the same track

Closes https://github.com/ppy/osu/issues/30190.
This commit is contained in:
Bartłomiej Dach 2024-10-11 12:40:45 +02:00
parent 0882f1bb70
commit 47cb696b69
No known key found for this signature in database
1 changed files with 55 additions and 39 deletions

View File

@ -74,6 +74,7 @@ public partial class MusicController : CompositeDrawable
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,69 @@ private bool next(bool allowProtectedTracks)
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)]; result = previousRandomSets[^1];
break; 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()