diff --git a/Qsor.Game/Beatmaps/BeatmapManager.cs b/Qsor.Game/Beatmaps/BeatmapManager.cs index 7c757ff..0bc8ad6 100644 --- a/Qsor.Game/Beatmaps/BeatmapManager.cs +++ b/Qsor.Game/Beatmaps/BeatmapManager.cs @@ -1,4 +1,7 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using ICSharpCode.SharpZipLib.Zip; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -32,6 +35,9 @@ public class BeatmapManager [Resolved] private QsorConfigManager ConfigManager { get; set; } + [Resolved] + private QsorDbContextFactory DbContextFactory { get; set; } + public BackgroundImageContainer Background; public BeatmapManager() @@ -121,5 +127,69 @@ public BeatmapContainer LoadBeatmap(Storage storage, string fileName) return new BeatmapContainer(WorkingBeatmap); } + + private readonly List _alreadyRandomized = new(); + + public BeatmapContainer NextRandomMap() + { + WorkingBeatmap.Value?.Track.Stop(); + + var ctx = DbContextFactory.Get(); + while (true) + { + var beatmapModel = ctx.Beatmaps.Where(s => !_alreadyRandomized.Contains(s.Id)) + .ToList() + .OrderBy(_ => Guid.NewGuid()) + .FirstOrDefault(); + + // Never repeating beatmaps + if (beatmapModel == null) + { + // We do not have a single beatmap we could use + if (_alreadyRandomized.Count <= 0) + { + return null; + } + + _alreadyRandomized.Clear(); + continue; + } + + var beatmapStorage = Storage.GetStorageForDirectory(beatmapModel?.Path); + _alreadyRandomized.Add(beatmapModel.Id); + return LoadBeatmap(beatmapStorage, beatmapModel.File); + } + } + + public BeatmapContainer PreviousRandomMap() + { + var ctx = DbContextFactory.Get(); + + while (!ctx.Beatmaps.Any(s => s.Id == _alreadyRandomized.LastOrDefault())) + { + if (_alreadyRandomized.Count <= 0) + { + // We don't have any beatmaps to randomize take the next one + return NextRandomMap(); + } + + // Beatmap deleted, lets try again + _alreadyRandomized.RemoveAt(_alreadyRandomized.Count - 1); + } + + _alreadyRandomized.RemoveAt(_alreadyRandomized.Count - 1); // Pop back + + var beatmapModel = ctx.Beatmaps + .FirstOrDefault(s => s.Id == _alreadyRandomized.LastOrDefault()); + + if (beatmapModel != null) + { + var beatmapStorage = Storage.GetStorageForDirectory(beatmapModel.Path); + return LoadBeatmap(beatmapStorage, beatmapModel.File); + } + + // Couldn't find the last map, get the next map instead! + return NextRandomMap(); + } } } \ No newline at end of file diff --git a/Qsor.Game/Beatmaps/BeatmapParser.cs b/Qsor.Game/Beatmaps/BeatmapParser.cs index 1619a58..f4d65ea 100644 --- a/Qsor.Game/Beatmaps/BeatmapParser.cs +++ b/Qsor.Game/Beatmaps/BeatmapParser.cs @@ -133,7 +133,6 @@ public T ConstructFromString(string content) { Offset = double.Parse(tPoint[0].Trim()), MsPerBeat = double.Parse(tPoint[1].Trim()), - _MsPerBeat = double.Parse(tPoint[1].Trim()), Meter = int.Parse(tPoint[2].Trim()), SampleSet = int.Parse(tPoint[3].Trim()), SampleIndex = int.Parse(tPoint[4].Trim()), @@ -155,10 +154,6 @@ public T ConstructFromString(string content) } timingPoint.Parent = bm.TimingPoints.Last().Parent ?? bm.TimingPoints.Last(); - - var sliderVelocity = -100 / timingPoint.MsPerBeat; - timingPoint.MsPerBeat = timingPoint.Parent.MsPerBeat / sliderVelocity; - timingPoint.Meter = timingPoint.Parent.Meter; } bm.TimingPoints.Add(timingPoint); diff --git a/Qsor.Game/Beatmaps/TimingPoint.cs b/Qsor.Game/Beatmaps/TimingPoint.cs index e00e528..a905bd1 100644 --- a/Qsor.Game/Beatmaps/TimingPoint.cs +++ b/Qsor.Game/Beatmaps/TimingPoint.cs @@ -6,7 +6,6 @@ public class TimingPoint { public double Offset; public double MsPerBeat; - public double _MsPerBeat; // Internal use public int Meter; public int SampleSet; public int SampleIndex; diff --git a/Qsor.Game/Graphics/Containers/BeatSyncedContainer.cs b/Qsor.Game/Graphics/Containers/BeatSyncedContainer.cs index ae54170..8ebb7e9 100644 --- a/Qsor.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/Qsor.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -52,6 +52,7 @@ public class BeatSyncedContainer : Container private ChannelAmplitudes _defaultAmplitudes; protected bool IsBeatSyncedWithTrack { get; private set; } + protected bool IsTrackPaused => Beatmap?.Value?.Track?.IsRunning ?? true; private TimingPoint _lastValidTimingPoint; @@ -79,22 +80,29 @@ protected override void Update() timingPoint ??= _defaultTiming; - IsBeatSyncedWithTrack = timingPoint._MsPerBeat > 0; + IsBeatSyncedWithTrack = timingPoint.MsPerBeat > 0; if (!IsBeatSyncedWithTrack) { - // Keep kiai - _lastValidTimingPoint.KiaiMode = timingPoint.KiaiMode; + // inherit kiai mode + if (_lastValidTimingPoint != null && timingPoint != null && timingPoint != _defaultTiming) + _lastValidTimingPoint.KiaiMode = timingPoint.KiaiMode; currentTrackTime = Clock.CurrentTime; - timingPoint = _lastValidTimingPoint; + timingPoint = _lastValidTimingPoint ?? _defaultTiming; } else { _lastValidTimingPoint = timingPoint; } + + if (!track?.IsRunning ?? true) + { + currentTrackTime = Clock.CurrentTime; + timingPoint = _defaultTiming; + } - var beatLength = timingPoint._MsPerBeat / Divisor; + var beatLength = timingPoint.MsPerBeat / Divisor; while (beatLength < MinimumBeatLength) beatLength *= 2; diff --git a/Qsor.Game/Graphics/UserInterface/Overlays/MusicPlayerOverlay.cs b/Qsor.Game/Graphics/UserInterface/Overlays/MusicPlayerOverlay.cs index 3ade601..4b90119 100644 --- a/Qsor.Game/Graphics/UserInterface/Overlays/MusicPlayerOverlay.cs +++ b/Qsor.Game/Graphics/UserInterface/Overlays/MusicPlayerOverlay.cs @@ -56,7 +56,10 @@ private void Load(NotificationOverlay notificationOverlay, BeatmapManager beatma ClickEvent = e => { - notificationOverlay.AddBigNotification("<< Prev !Unimplemented!", 1100); + notificationOverlay.AddBigNotification("<< Prev", 1100); + + beatmapManager?.PreviousRandomMap(); + beatmapManager?.WorkingBeatmap?.Value?.Play(); return true; } @@ -151,7 +154,11 @@ private void Load(NotificationOverlay notificationOverlay, BeatmapManager beatma ClickEvent = e => { - notificationOverlay.AddBigNotification(">> Next !Unimplemented!", 1100); + notificationOverlay.AddBigNotification(">> Next", 1100); + + beatmapManager?.NextRandomMap(); + beatmapManager?.WorkingBeatmap?.Value?.Play(); + return true; } }); diff --git a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableMenuSideFlashes.cs b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableMenuSideFlashes.cs index d6ed2ba..c909413 100644 --- a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableMenuSideFlashes.cs +++ b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableMenuSideFlashes.cs @@ -78,21 +78,15 @@ private void Load(BeatmapManager beatmapManager) _rightBox.Colour = ColourInfo.GradientHorizontal(gradientDark, gradientLight); } - private int _customBeatIndex = 0; protected override void OnNewBeat(int beatIndex, TimingPoint timingPoint, ChannelAmplitudes amplitudes) { var meter = timingPoint.Parent?.Meter ?? timingPoint.Meter; - if ((beatIndex < 0 || meter < 0) && !timingPoint.KiaiMode) + if (beatIndex < 0 || meter <= 0) return; - if (timingPoint.KiaiMode) - { - _customBeatIndex += 1; - } - - if (timingPoint.KiaiMode ? _customBeatIndex % 2 == 0 : beatIndex % meter == 0) + if (timingPoint.KiaiMode ? beatIndex % 2 == 0 : beatIndex % meter == 0) Flash(_leftBox, timingPoint.MsPerBeat, timingPoint.KiaiMode, amplitudes); - if (timingPoint.KiaiMode ? _customBeatIndex % 2 == 1 : beatIndex % meter == 0) + if (timingPoint.KiaiMode ? beatIndex % 2 == 1 : beatIndex % meter == 0) Flash(_rightBox, timingPoint.MsPerBeat, timingPoint.KiaiMode, amplitudes); } diff --git a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableQsorLogo.cs b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableQsorLogo.cs index 801c927..8253b20 100644 --- a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableQsorLogo.cs +++ b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/Drawables/DrawableQsorLogo.cs @@ -94,14 +94,14 @@ protected override void Update() const float scaleAdjustCutoff = 0.4f; - if (Beatmap.Value?.Track?.IsRunning == true) + if (!IsTrackPaused) { var maxAmplitude = _lastBeatIndex >= 0 ? Beatmap.Value.Track.CurrentAmplitudes.Maximum : 0; _qsorLogo.ScaleTo(1 - Math.Max(0, maxAmplitude - scaleAdjustCutoff) * 0.04f, 75, Easing.OutQuint); _ghostingLogo.ScaleTo(1 + Math.Max(0, maxAmplitude - scaleAdjustCutoff) * 0.04f, 75, Easing.OutQuint); } - + _visualisation.Scale = _qsorLogo.Scale; _visualisation.Size = _qsorLogo.Size; } @@ -126,7 +126,8 @@ protected override void OnNewBeat(int beatIndex, TimingPoint timingPoint, Channe _ripple .ScaleTo(_qsorLogo.Scale) .ScaleTo(_qsorLogo.Scale * (1 + 0.16f * amplitudeAdjust), timingPoint.MsPerBeat, Easing.OutQuint) - .FadeTo(0.3f * amplitudeAdjust).FadeOut(timingPoint.MsPerBeat, Easing.OutQuint); + .FadeTo(0.3f * amplitudeAdjust) + .FadeOut(timingPoint.MsPerBeat, Easing.OutQuint); if (timingPoint.KiaiMode) { diff --git a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/MainMenuScreen.cs b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/MainMenuScreen.cs index d7b9c75..2b3b52e 100644 --- a/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/MainMenuScreen.cs +++ b/Qsor.Game/Graphics/UserInterface/Screens/MainMenu/MainMenuScreen.cs @@ -59,23 +59,18 @@ private void Load(UpdaterOverlay updaterOverlay, AudioManager audioManager, Qsor FillMode = FillMode.Fill, }); AddInternal(parallaxBack); - - - - - var db = ctxFactory.Get(); - var beatmapModel = db.Beatmaps.ToList().OrderBy(r => Guid.NewGuid()).FirstOrDefault(); - var beatmapStorage = Storage.GetStorageForDirectory(beatmapModel?.Path); - beatmapManager.LoadBeatmap(beatmapStorage, beatmapModel?.File); - LoadComponent(beatmapManager.WorkingBeatmap.Value); + _workingBeatmap.BindTo(beatmapManager.WorkingBeatmap); + _workingBeatmap.ValueChanged += e => + { + if (e.NewValue == null) + return; + + LoadComponent(e.NewValue); - _background.SetTexture(_workingBeatmap.Value.Background); - - audioManager.AddItem(_workingBeatmap.Value.Track); - - - + _background.SetTexture(e.NewValue.Background); + audioManager.AddItem(e.NewValue.Track); + }; var parallaxFront = new ParallaxContainer { @@ -103,13 +98,6 @@ private void Load(UpdaterOverlay updaterOverlay, AudioManager audioManager, Qsor AddInternal(updaterOverlay); } - protected override void LoadComplete() - { - _workingBeatmap.Value.Play(); - - base.LoadComplete(); - } - public override void OnEntering(IScreen last) { _clock.Start();