diff --git a/osu-framework b/osu-framework index 2a3b245da9..6b44a9f807 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 2a3b245da9eff604be09d473203f829690d2808c +Subproject commit 6b44a9f807fadcb3b3f044780d7e27d62ffe80ac diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs index 909ee9b134..5e9b976c8a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatSyncedContainer.cs @@ -151,6 +151,8 @@ namespace osu.Desktop.VisualTests.Tests private int calculateBeatCount(TimingControlPoint current) { + if (timingPoints.Count == 0) return 0; + if (timingPoints[timingPoints.Count - 1] == current) return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength); diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index 954d24fcc1..088ccb2aa3 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -81,7 +81,7 @@ namespace osu.Desktop.VisualTests.Tests { return new Player { - Beatmap = beatmap + InitialBeatmap = beatmap }; } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs index 775bfe0f03..4a980068b9 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseResults.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseResults.cs @@ -59,7 +59,7 @@ namespace osu.Desktop.VisualTests.Tests } }) { - Beatmap = beatmap + InitialBeatmap = beatmap }); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index dcb2a29556..b92c0916b1 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.UI { base.ApplyBeatmap(); - PreferredColumns = (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize); + PreferredColumns = (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)); } protected override void ApplySpeedAdjustments() diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 0d1dc21e96..115bba5d9d 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -5,7 +5,7 @@ using System; namespace osu.Game.Beatmaps.ControlPoints { - public class ControlPoint : IComparable + public class ControlPoint : IComparable, IEquatable { /// /// The time at which the control point takes effect. @@ -13,5 +13,7 @@ namespace osu.Game.Beatmaps.ControlPoints public double Time; public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time); + + public bool Equals(ControlPoint other) => Time.Equals(other?.Time); } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs new file mode 100644 index 0000000000..970a3dbd35 --- /dev/null +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Database; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; + +namespace osu.Game.Beatmaps +{ + internal class DummyWorkingBeatmap : WorkingBeatmap + { + private readonly OsuGameBase game; + + public DummyWorkingBeatmap(OsuGameBase game) + : base(new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "please load a beatmap!", + Title = "no beatmaps available!", + Author = "no one", + }, + BeatmapSet = new BeatmapSetInfo(), + Difficulty = new BeatmapDifficulty + { + DrainRate = 0, + CircleSize = 0, + OverallDifficulty = 0, + ApproachRate = 0, + SliderMultiplier = 0, + SliderTickRate = 0, + }, + Ruleset = new DummyRulesetInfo() + }) + { + this.game = game; + } + + protected override Beatmap GetBeatmap() => new Beatmap + { + HitObjects = new List(), + }; + + protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4"); + + protected override Track GetTrack() => new TrackVirtual(); + + private class DummyRulesetInfo : RulesetInfo + { + public override Ruleset CreateInstance() => new DummyRuleset(); + + private class DummyRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; + + public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) + { + throw new NotImplementedException(); + } + + public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => null; + + public override ScoreProcessor CreateScoreProcessor() + { + throw new NotImplementedException(); + } + + public override string Description => "dummy"; + + public override IEnumerable CreateGameplayKeys() => new List(); + } + } + } +} diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 43769f67b5..b31b71a728 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -28,6 +28,11 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private BeatmapIPCChannel ipc; + /// + /// A default representation of a WorkingBeatmap to use when no beatmap is available. + /// + public WorkingBeatmap DefaultBeatmap { private get; set; } + public BeatmapDatabase(Storage storage, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) : base(storage, connection) { this.rulesets = rulesets; @@ -274,6 +279,9 @@ namespace osu.Game.Database public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false) { + if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) + return DefaultBeatmap; + if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null) beatmapInfo = GetChildren(beatmapInfo, true); diff --git a/osu.Game/Database/RulesetInfo.cs b/osu.Game/Database/RulesetInfo.cs index 322cb10c33..3990f9e8ae 100644 --- a/osu.Game/Database/RulesetInfo.cs +++ b/osu.Game/Database/RulesetInfo.cs @@ -21,6 +21,6 @@ namespace osu.Game.Database [Indexed] public bool Available { get; set; } - public Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); + public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); } } \ No newline at end of file diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 5d8a5753b0..123ef0662d 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -35,10 +35,12 @@ namespace osu.Game.Graphics.Containers protected override void Update() { - if (Beatmap.Value?.Track == null) + var track = Beatmap.Value.Track; + + if (track == null) return; - double currentTrackTime = Beatmap.Value.Track.CurrentTime + EarlyActivationMilliseconds; + double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; TimingControlPoint timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); EffectControlPoint effectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); @@ -58,7 +60,7 @@ namespace osu.Game.Graphics.Containers TimeSinceLastBeat = timingPoint.BeatLength - TimeUntilNextBeat; - if (timingPoint == lastTimingPoint && beatIndex == lastBeat) + if (timingPoint.Equals(lastTimingPoint) && beatIndex == lastBeat) return; using (BeginDelayedSequence(-TimeSinceLastBeat, true)) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 71ac8af08d..939c4a4915 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -43,7 +43,7 @@ namespace osu.Game protected MenuCursor Cursor; - public readonly Bindable Beatmap = new Bindable(); + public Bindable Beatmap { get; private set; } private Bindable fpsDisplayVisible; @@ -121,6 +121,10 @@ namespace osu.Game Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); + var defaultBeatmap = new DummyWorkingBeatmap(this); + Beatmap = new NonNullableBindable(defaultBeatmap); + BeatmapDatabase.DefaultBeatmap = defaultBeatmap; + OszArchiveReader.Register(); Dependencies.Cache(API = new APIAccess diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index e5dc66fc75..87c0afebf7 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -83,11 +83,12 @@ namespace osu.Game.Overlays.Music }, }; - list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren().ToList(); + list.BeatmapSets = BeatmapSets = beatmaps.GetAllWithChildren(b => !b.DeletePending).ToList(); beatmapBacking.BindTo(game.Beatmap); - filter.Search.OnCommit = (sender, newText) => { + filter.Search.OnCommit = (sender, newText) => + { var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); if (beatmap != null) playSpecified(beatmap); }; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index ade860f358..7e2c0305a9 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using OpenTK; using osu.Game.Beatmaps; using osu.Game.Graphics.Backgrounds; @@ -26,11 +27,12 @@ namespace osu.Game.Screens.Backgrounds { if (beatmap == value && beatmap != null) return; + beatmap = value; Schedule(() => { - var newBackground = beatmap == null ? new Background(@"Backgrounds/bg1") : new BeatmapBackground(beatmap); + var newBackground = new BeatmapBackground(beatmap); LoadComponentAsync(newBackground, delegate { @@ -51,7 +53,7 @@ namespace osu.Game.Screens.Backgrounds } } - public BackgroundScreenBeatmap(WorkingBeatmap beatmap) + public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null) { Beatmap = beatmap; } @@ -80,9 +82,9 @@ namespace osu.Game.Screens.Backgrounds } [BackgroundDependencyLoader] - private void load() + private void load(TextureStore textures) { - Sprite.Texture = beatmap?.Background; + Sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg1"); } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 2582c68296..7ec8347a8f 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit protected override void OnResuming(Screen last) { - Beatmap?.Track?.Stop(); + Beatmap.Value.Track?.Stop(); base.OnResuming(last); } @@ -26,13 +26,13 @@ namespace osu.Game.Screens.Edit { base.OnEntering(last); Background.FadeColour(Color4.DarkGray, 500); - Beatmap?.Track?.Stop(); + Beatmap.Value.Track?.Stop(); } protected override bool OnExiting(Screen next) { Background.FadeColour(Color4.White, 500); - Beatmap?.Track?.Start(); + Beatmap.Value.Track?.Start(); return base.OnExiting(next); } } diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 5791b7f196..4ab157db05 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -103,9 +103,9 @@ namespace osu.Game.Screens.Menu } beatmaps.GetChildren(setInfo); - Beatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); - track = Beatmap.Track; + track = Beatmap.Value.Track; trackManager.SetExclusive(track); welcome = audio.Sample.Get(@"welcome"); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 7fa1eda880..2de4c1b21f 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -4,7 +4,6 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.ES30; -using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Batches; @@ -16,6 +15,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Graphics; using System; +using osu.Framework.Allocation; namespace osu.Game.Screens.Menu { @@ -76,23 +76,24 @@ namespace osu.Game.Screens.Menu BlendingMode = BlendingMode.Additive; } - [BackgroundDependencyLoader(true)] - private void load(ShaderManager shaders, OsuGame game) + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, OsuGameBase game) { - if (game?.Beatmap != null) - beatmap.BindTo(game.Beatmap); - shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + beatmap.BindTo(game.Beatmap); + shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); } private void updateAmplitudes() { - float[] temporalAmplitudes = beatmap.Value?.Track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + var track = beatmap.Value.Track; - var effect = beatmap.Value?.Beatmap.ControlPointInfo.EffectPointAt(beatmap.Value.Track?.CurrentTime ?? Time.Current); + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + + var effect = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current); for (int i = 0; i < bars_per_visualiser; i++) { - if (beatmap?.Value?.Track?.IsRunning ?? false) + if (track?.IsRunning ?? false) { float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f); if (targetAmplitude > frequencyAmplitudes[i]) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 42d435ef88..df9b304a47 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -69,12 +69,6 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); } - protected override void OnBeatmapChanged(WorkingBeatmap beatmap) - { - base.OnBeatmapChanged(beatmap); - background.Next(); - } - private void preloadSongSelect() { if (songSelect == null) @@ -92,16 +86,30 @@ namespace osu.Game.Screens.Menu { base.OnEntering(last); buttons.FadeInFromZero(500); - if (last is Intro && Beatmap != null) + + var track = Beatmap.Value.Track; + var metadata = Beatmap.Value.Metadata; + + if (last is Intro && track != null) { - if (!Beatmap.Track.IsRunning) + if (!track.IsRunning) { - Beatmap.Track.Seek(Beatmap.Metadata.PreviewTime); - if (Beatmap.Metadata.PreviewTime == -1) - Beatmap.Track.Seek(Beatmap.Track.Length * 0.4f); - Beatmap.Track.Start(); + track.Seek(metadata.PreviewTime); + if (metadata.PreviewTime == -1) + track.Seek(track.Length * 0.4f); + track.Start(); } } + + Beatmap.ValueChanged += beatmap_ValueChanged; + } + + private void beatmap_ValueChanged(WorkingBeatmap newValue) + { + if (!IsCurrentScreen) + return; + + background.Next(); } protected override void OnSuspending(Screen next) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index efbd106cb5..ecfd98688e 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -275,7 +275,7 @@ namespace osu.Game.Screens.Menu const float scale_adjust_cutoff = 0.4f; const float velocity_adjust_cutoff = 0.98f; - var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value?.Track?.CurrentAmplitudes.Maximum ?? 0 : 0; + var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track?.CurrentAmplitudes.Maximum ?? 0 : 0; logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, EasingTypes.OutQuint); if (maxAmplitude > velocity_adjust_cutoff) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 64223db100..d916614abd 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Screens; @@ -35,34 +36,31 @@ namespace osu.Game.Screens /// internal virtual bool AllowBeatmapRulesetChange => true; - private readonly Bindable beatmap = new Bindable(); + protected readonly Bindable Beatmap = new Bindable(); + + public WorkingBeatmap InitialBeatmap + { + set + { + if (IsLoaded) throw new InvalidOperationException($"Cannot set {nameof(InitialBeatmap)} post-load."); + Beatmap.Value = value; + } + } private readonly Bindable ruleset = new Bindable(); private SampleChannel sampleExit; - public WorkingBeatmap Beatmap - { - get - { - return beatmap.Value; - } - set - { - beatmap.Value = value; - } - } - [BackgroundDependencyLoader(permitNulls: true)] private void load(OsuGameBase game, OsuGame osuGame, AudioManager audio) { if (game != null) { //if we were given a beatmap at ctor time, we want to pass this on to the game-wide beatmap. - var localMap = beatmap.Value; - beatmap.BindTo(game.Beatmap); + var localMap = Beatmap.Value; + Beatmap.BindTo(game.Beatmap); if (localMap != null) - beatmap.Value = localMap; + Beatmap.Value = localMap; } if (osuGame != null) @@ -71,20 +69,6 @@ namespace osu.Game.Screens sampleExit = audio.Sample.Get(@"UI/melodic-1"); } - protected override void LoadComplete() - { - base.LoadComplete(); - - beatmap.ValueChanged += OnBeatmapChanged; - } - - /// - /// The global Beatmap was changed. - /// - protected virtual void OnBeatmapChanged(WorkingBeatmap beatmap) - { - } - protected override void Update() { if (!IsCurrentScreen) return; @@ -94,7 +78,7 @@ namespace osu.Game.Screens // we only want to apply these restrictions when we are inside a screen stack. // the use case for not applying is in visual/unit tests. ruleset.Disabled = !AllowBeatmapRulesetChange; - beatmap.Disabled = !AllowBeatmapRulesetChange; + Beatmap.Disabled = !AllowBeatmapRulesetChange; } } @@ -110,8 +94,6 @@ namespace osu.Game.Screens BackgroundScreen bg = CreateBackground(); - OnBeatmapChanged(Beatmap); - if (lastOsu?.Background != null) { if (bg == null || lastOsu.Background.Equals(bg)) @@ -156,11 +138,7 @@ namespace osu.Game.Screens if (base.OnExiting(next)) return true; - // while this is not necessary as we are constructing our own bindable, there are cases where - // the GC doesn't run as fast as expected and this is triggered post-exit. - // added to resolve https://github.com/ppy/osu/issues/829 - beatmap.ValueChanged -= OnBeatmapChanged; - + Beatmap.UnbindAll(); return false; } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c9ca91faa0..7bd310e168 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Play private HUDOverlay hudOverlay; private FailOverlay failOverlay; + private bool loadedSuccessfully => HitRenderer?.Objects.Any() == true; + [BackgroundDependencyLoader(permitNulls: true)] private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config, OsuGame osu) { @@ -81,24 +83,25 @@ namespace osu.Game.Screens.Play try { - if (Beatmap == null) - Beatmap = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true); + if (!Beatmap.Value.WithStoryboard) + // we need to ensure the storyboard is loaded. + Beatmap.Value = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true); - if (Beatmap?.Beatmap == null) + if (Beatmap.Value.Beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - ruleset = osu?.Ruleset.Value ?? Beatmap.BeatmapInfo.Ruleset; + ruleset = osu?.Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); try { - HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.BeatmapInfo.Ruleset.ID); + HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. - ruleset = Beatmap.BeatmapInfo.Ruleset; + ruleset = Beatmap.Value.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); } @@ -115,7 +118,7 @@ namespace osu.Game.Screens.Play return; } - Track track = Beatmap.Track; + Track track = Beatmap.Value.Track; if (track != null) { @@ -128,7 +131,7 @@ namespace osu.Game.Screens.Play decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = HitRenderer.Objects.First().StartTime; - decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.BeatmapInfo.AudioLeadIn))); + decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.Value.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); @@ -141,7 +144,7 @@ namespace osu.Game.Screens.Play { adjustableSourceClock.Reset(); - foreach (var mod in Beatmap.Mods.Value.OfType()) + foreach (var mod in Beatmap.Value.Mods.Value.OfType()) mod.ApplyToClock(adjustableSourceClock); decoupledClock.ChangeSource(adjustableSourceClock); @@ -209,7 +212,7 @@ namespace osu.Game.Screens.Play hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); - hudOverlay.ModDisplay.Current.BindTo(Beatmap.Mods); + hudOverlay.ModDisplay.Current.BindTo(Beatmap.Value.Mods); //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) HitRenderer.OnAllJudged += onCompletion; @@ -242,7 +245,7 @@ namespace osu.Game.Screens.Play { var score = new Score { - Beatmap = Beatmap.BeatmapInfo, + Beatmap = Beatmap.Value.BeatmapInfo, Ruleset = ruleset }; scoreProcessor.PopulateScore(score); @@ -265,6 +268,9 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); + if (!loadedSuccessfully) + return; + (Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1500, EasingTypes.OutQuint); Background?.FadeTo(1 - (float)dimLevel, 1500, EasingTypes.OutQuint); @@ -304,7 +310,11 @@ namespace osu.Game.Screens.Play return base.OnExiting(next); } - pauseContainer.Pause(); + if (loadedSuccessfully) + { + pauseContainer.Pause(); + } + return true; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f2ed378e7c..cdb608e74d 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -35,7 +35,8 @@ namespace osu.Game.Screens.Play { this.player = player; - player.RestartRequested = () => { + player.RestartRequested = () => + { showOverlays = false; ValidForResume = true; }; @@ -74,7 +75,6 @@ namespace osu.Game.Screens.Play { RestartCount = player.RestartCount + 1, RestartRequested = player.RestartRequested, - Beatmap = player.Beatmap, }); Delay(400); diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 636851e14d..93f6fec862 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Ranking { RelativeSizeAxes = Axes.Both, Alpha = 0.2f, - Texture = Beatmap?.Background, + Texture = Beatmap.Value.Background, Anchor = Anchor.Centre, Origin = Anchor.Centre, FillMode = FillMode.Fill diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 647cac5bbe..e135fefc6d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -137,6 +137,7 @@ namespace osu.Game.Screens.Select { selectedGroup = null; selectedPanel = null; + SelectionChanged?.Invoke(null); return; } @@ -284,6 +285,7 @@ namespace osu.Game.Screens.Select private void load(BeatmapDatabase database, OsuConfigManager config) { this.database = database; + randomType = config.GetBindable(OsuSetting.SelectionRandomType); } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 42f9598096..8ba450720b 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -45,6 +45,12 @@ namespace osu.Game.Screens.Select }; } + protected override void LoadComplete() + { + base.LoadComplete(); + AlwaysPresent = true; + } + protected override bool HideOnEscape => false; protected override bool BlockPassThroughMouse => false; @@ -63,18 +69,6 @@ namespace osu.Game.Screens.Select public void UpdateBeatmap(WorkingBeatmap beatmap) { - if (beatmap?.BeatmapInfo == null) - { - State = Visibility.Hidden; - beatmapInfoContainer?.FadeOut(250); - beatmapInfoContainer?.Expire(); - beatmapInfoContainer = null; - return; - } - - State = Visibility.Visible; - AlwaysPresent = true; - var lastContainer = beatmapInfoContainer; float newDepth = lastContainer?.Depth + 1 ?? 0; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index f96fbb87cb..51f570e901 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -51,28 +51,27 @@ namespace osu.Game.Screens.Select ValidForResume = false; Push(new Editor()); }, Key.Number3); + + Beatmap.ValueChanged += beatmap_ValueChanged; } - protected override void OnBeatmapChanged(WorkingBeatmap beatmap) + private void beatmap_ValueChanged(WorkingBeatmap beatmap) { - beatmap?.Mods.BindTo(modSelect.SelectedMods); + if (!IsCurrentScreen) return; - if (Beatmap?.Track != null) - Beatmap.Track.Looping = false; + beatmap.Mods.BindTo(modSelect.SelectedMods); beatmapDetails.Beatmap = beatmap; - if (beatmap?.Track != null) + if (beatmap.Track != null) beatmap.Track.Looping = true; - - base.OnBeatmapChanged(beatmap); } protected override void OnResuming(Screen last) { player = null; - Beatmap.Track.Looping = true; + Beatmap.Value.Track.Looping = true; base.OnResuming(last); } @@ -95,8 +94,8 @@ namespace osu.Game.Screens.Select if (base.OnExiting(next)) return true; - if (Beatmap?.Track != null) - Beatmap.Track.Looping = false; + if (Beatmap.Value.Track != null) + Beatmap.Value.Track.Looping = false; return false; } @@ -105,12 +104,9 @@ namespace osu.Game.Screens.Select { if (player != null) return; - Beatmap.Track.Looping = false; + Beatmap.Value.Track.Looping = false; - LoadComponentAsync(player = new PlayerLoader(new Player - { - Beatmap = Beatmap, //eagerly set this so it's present before push. - }), l => Push(player)); + LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player)); } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 68f239fb0a..d28a84217b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Threading; using OpenTK; using OpenTK.Input; @@ -28,7 +29,7 @@ namespace osu.Game.Screens.Select { private readonly Bindable ruleset = new Bindable(); private BeatmapDatabase database; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); + protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(); private readonly BeatmapCarousel carousel; private TrackManager trackManager; @@ -107,8 +108,9 @@ namespace osu.Game.Screens.Select Size = new Vector2(carousel_width, 1), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - SelectionChanged = selectionChanged, - StartRequested = raiseSelect + SelectionChanged = carouselSelectionChanged, + BeatmapsChanged = carouselBeatmapsLoaded, + StartRequested = carouselRaisedStart }); Add(FilterControl = new FilterControl { @@ -127,7 +129,6 @@ namespace osu.Game.Screens.Select Top = left_area_padding, Right = left_area_padding, }, - X = -50, }); if (ShowFooter) @@ -146,7 +147,7 @@ namespace osu.Game.Screens.Select Add(Footer = new Footer { OnBack = Exit, - OnStart = raiseSelect, + OnStart = carouselRaisedStart, }); FooterPanels.Add(BeatmapOptions = new BeatmapOptionsOverlay()); @@ -181,34 +182,79 @@ namespace osu.Game.Screens.Select initialAddSetsTask = new CancellationTokenSource(); - carousel.BeatmapsChanged = beatmapsLoaded; carousel.Beatmaps = database.GetAllWithChildren(b => !b.DeletePending); + + Beatmap.ValueChanged += beatmap_ValueChanged; } - private void beatmapsLoaded() + private void carouselBeatmapsLoaded() { - if (Beatmap != null) - carousel.SelectBeatmap(Beatmap.BeatmapInfo, false); + if (Beatmap.Value != null && !Beatmap.Value.BeatmapSetInfo.DeletePending) + carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false); else carousel.SelectNext(); } - private void raiseSelect() + private void carouselRaisedStart() { var pendingSelection = selectionChangedDebounce; selectionChangedDebounce = null; if (pendingSelection?.Completed == false) { - pendingSelection?.RunTask(); - pendingSelection?.Cancel(); // cancel the already scheduled task. + pendingSelection.RunTask(); + pendingSelection.Cancel(); // cancel the already scheduled task. } - if (Beatmap == null) return; - OnSelected(); } + private ScheduledDelegate selectionChangedDebounce; + + // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. + private BeatmapInfo beatmapNoDebounce; + + /// + /// selection has been changed as the result of interaction with the carousel. + /// + private void carouselSelectionChanged(BeatmapInfo beatmap) + { + Action performLoad = delegate + { + bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID; + + Beatmap.Value = database.GetWorkingBeatmap(beatmap, Beatmap); + + ensurePlayingSelected(preview); + changeBackground(Beatmap.Value); + }; + + if (beatmap == null) + { + if (!Beatmap.IsDefault) + performLoad(); + } + else + { + selectionChangedDebounce?.Cancel(); + + if (beatmap.Equals(beatmapNoDebounce)) + return; + + if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) + sampleChangeDifficulty.Play(); + else + sampleChangeBeatmap.Play(); + + beatmapNoDebounce = beatmap; + + if (beatmap == Beatmap.Value.BeatmapInfo) + performLoad(); + else + selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 100); + } + } + private void triggerRandom(UserInputManager input) { if (input.CurrentState.Keyboard.ShiftPressed) @@ -231,23 +277,27 @@ namespace osu.Game.Screens.Select protected override void OnEntering(Screen last) { base.OnEntering(last); - ensurePlayingSelected(); - - changeBackground(Beatmap); - - selectionChangeNoBounce = Beatmap?.BeatmapInfo; Content.FadeInFromZero(250); - beatmapInfoWedge.State = Visibility.Visible; - FilterControl.Activate(); } + private void beatmap_ValueChanged(WorkingBeatmap beatmap) + { + if (!IsCurrentScreen) return; + + carousel.SelectBeatmap(beatmap?.BeatmapInfo); + } + protected override void OnResuming(Screen last) { - changeBackground(Beatmap); - ensurePlayingSelected(); + if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending) + { + changeBackground(Beatmap); + ensurePlayingSelected(); + } + base.OnResuming(last); Content.FadeIn(250); @@ -300,70 +350,25 @@ namespace osu.Game.Screens.Select backgroundModeBeatmap.FadeTo(1, 250); } + beatmapInfoWedge.State = Visibility.Visible; beatmapInfoWedge.UpdateBeatmap(beatmap); } - /// - /// The global Beatmap was changed. - /// - protected override void OnBeatmapChanged(WorkingBeatmap beatmap) - { - base.OnBeatmapChanged(beatmap); - - //todo: change background in selectionChanged instead; support per-difficulty backgrounds. - changeBackground(beatmap); - carousel.SelectBeatmap(beatmap?.BeatmapInfo); - } - - private ScheduledDelegate selectionChangedDebounce; - - // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. - private BeatmapInfo selectionChangeNoBounce; - - /// - /// selection has been changed as the result of interaction with the carousel. - /// - private void selectionChanged(BeatmapInfo beatmap) - { - selectionChangedDebounce?.Cancel(); - - if (beatmap.Equals(Beatmap?.BeatmapInfo)) - return; - - bool preview = beatmap.BeatmapSetInfoID != Beatmap?.BeatmapInfo.BeatmapSetInfoID; - - if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID) - sampleChangeDifficulty.Play(); - else - sampleChangeBeatmap.Play(); - - selectionChangeNoBounce = beatmap; - - selectionChangedDebounce = Scheduler.AddDelayed(delegate - { - Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap); - ensurePlayingSelected(preview); - }, 100); - } - private void ensurePlayingSelected(bool preview = false) { - Track track = Beatmap?.Track; + Track track = Beatmap.Value.Track; - if (track != null) - { - trackManager.SetExclusive(track); - if (preview) - track.Seek(Beatmap.Metadata.PreviewTime); - track.Start(); - } + trackManager.SetExclusive(track); + + if (preview) track.Seek(Beatmap.Value.Metadata.PreviewTime); + track.Start(); } private void removeBeatmapSet(BeatmapSetInfo beatmapSet) { carousel.RemoveBeatmap(beatmapSet); if (carousel.SelectedBeatmap == null) - Beatmap = null; + Beatmap.SetDefault(); } private void promptDelete() @@ -380,7 +385,7 @@ namespace osu.Game.Screens.Select { case Key.KeypadEnter: case Key.Enter: - raiseSelect(); + carouselRaisedStart(); return true; case Key.Delete: if (state.Keyboard.ShiftPressed) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 42b5765a6d..95b339afb5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -76,6 +76,7 @@ + @@ -538,4 +539,4 @@ --> - + \ No newline at end of file