diff --git a/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs index 9791554f02..c2e77d60ed 100644 --- a/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Modes.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -4,11 +4,15 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Catch.Objects; using System.Collections.Generic; +using System; +using osu.Game.Modes.Objects; namespace osu.Game.Modes.Catch.Beatmaps { internal class CatchBeatmapConverter : IBeatmapConverter { + public IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; + public Beatmap Convert(Beatmap original) { return new Beatmap(original) diff --git a/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs index 3ff210c1cc..21a324fc1c 100644 --- a/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Modes.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -4,11 +4,15 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Mania.Objects; using System.Collections.Generic; +using System; +using osu.Game.Modes.Objects; namespace osu.Game.Modes.Mania.Beatmaps { internal class ManiaBeatmapConverter : IBeatmapConverter { + public IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; + public Beatmap Convert(Beatmap original) { return new Beatmap(original) diff --git a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs index bae12a98e3..20ebebd521 100644 --- a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -9,11 +9,14 @@ using osu.Game.Modes.Osu.Objects.Drawables; using System.Collections.Generic; using osu.Game.Modes.Objects.Types; using System.Linq; +using System; namespace osu.Game.Modes.Osu.Beatmaps { internal class OsuBeatmapConverter : IBeatmapConverter { + public IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasPosition) }; + public Beatmap Convert(Beatmap original) { return new Beatmap(original) @@ -56,8 +59,9 @@ namespace osu.Game.Modes.Osu.Beatmaps { StartTime = original.StartTime, Samples = original.Samples, - Position = new Vector2(512, 384) / 2, - EndTime = endTimeData.EndTime + EndTime = endTimeData.EndTime, + + Position = positionData?.Position ?? new Vector2(512, 384) / 2, }; } diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs index aee06ad796..a56ca43805 100644 --- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -38,6 +38,8 @@ namespace osu.Game.Modes.Taiko.Beatmaps /// private const float taiko_base_distance = 100; + public IEnumerable ValidConversionTypes { get; } = new[] { typeof(HitObject) }; + public Beatmap Convert(Beatmap original) { BeatmapInfo info = original.BeatmapInfo.DeepClone(); diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs index 72b248cfba..fbd6a60327 100644 --- a/osu.Game/Beatmaps/IBeatmapConverter.cs +++ b/osu.Game/Beatmaps/IBeatmapConverter.cs @@ -1,6 +1,9 @@ // 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 System.Linq; using osu.Game.Modes.Objects; namespace osu.Game.Beatmaps @@ -11,6 +14,12 @@ namespace osu.Game.Beatmaps /// The type of HitObject stored in the Beatmap. public interface IBeatmapConverter where T : HitObject { + /// + /// The type of HitObjects that can be converted to be used for this Beatmap. + /// + /// + IEnumerable ValidConversionTypes { get; } + /// /// Converts a Beatmap to another mode. /// @@ -18,4 +27,16 @@ namespace osu.Game.Beatmaps /// The converted Beatmap. Beatmap Convert(Beatmap original); } + + public static class BeatmapConverterExtensions + { + /// + /// Checks if a Beatmap can be converted using a Beatmap Converter. + /// + /// The Converter to use. + /// The Beatmap to check. + /// Whether the Beatmap can be converted using . + public static bool CanConvert(this IBeatmapConverter converter, Beatmap beatmap) where TObject : HitObject + => converter.ValidConversionTypes.All(t => beatmap.HitObjects.Any(h => t.IsAssignableFrom(h.GetType()))); + } } diff --git a/osu.Game/Modes/Objects/Legacy/LegacySpinner.cs b/osu.Game/Modes/Objects/Legacy/LegacySpinner.cs index 8f65d5e8a1..196706f157 100644 --- a/osu.Game/Modes/Objects/Legacy/LegacySpinner.cs +++ b/osu.Game/Modes/Objects/Legacy/LegacySpinner.cs @@ -1,4 +1,5 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . +using OpenTK; +// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Modes.Objects.Types; @@ -8,10 +9,12 @@ namespace osu.Game.Modes.Objects.Legacy /// /// Legacy Spinner-type, used for parsing Beatmaps. /// - internal class LegacySpinner : HitObject, IHasEndTime + internal class LegacySpinner : HitObject, IHasEndTime, IHasPosition { public double EndTime { get; set; } public double Duration => EndTime - StartTime; + + public Vector2 Position { get; set; } } } diff --git a/osu.Game/Modes/Objects/LegacyHitObjectParser.cs b/osu.Game/Modes/Objects/LegacyHitObjectParser.cs index 2316e5dc5d..580c09c646 100644 --- a/osu.Game/Modes/Objects/LegacyHitObjectParser.cs +++ b/osu.Game/Modes/Objects/LegacyHitObjectParser.cs @@ -100,6 +100,7 @@ namespace osu.Game.Modes.Objects { result = new LegacySpinner { + Position = new Vector2(512, 384) / 2, EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture) }; diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 6962c80d87..98a6c35135 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -122,6 +122,10 @@ namespace osu.Game.Modes.UI IBeatmapConverter converter = CreateBeatmapConverter(); IBeatmapProcessor processor = CreateBeatmapProcessor(); + // Check if the beatmap can be converted + if (!converter.CanConvert(beatmap.Beatmap)) + throw new BeatmapInvalidForModeException($"{nameof(Beatmap)} can't be converted to the current mode."); + // Convert the beatmap Beatmap = converter.Convert(beatmap.Beatmap); @@ -136,7 +140,6 @@ namespace osu.Game.Modes.UI applyMods(beatmap.Mods.Value); } - /// /// Applies the active mods to this HitRenderer. /// @@ -268,4 +271,12 @@ namespace osu.Game.Modes.UI /// The Playfield. protected abstract Playfield CreatePlayfield(); } + + public class BeatmapInvalidForModeException : Exception + { + public BeatmapInvalidForModeException(string text) + : base(text) + { + } + } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b32548c31a..7c95a09e31 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -60,8 +60,8 @@ namespace osu.Game.Screens.Play private PauseOverlay pauseOverlay; private FailOverlay failOverlay; - [BackgroundDependencyLoader] - private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config) + [BackgroundDependencyLoader(permitNulls: true)] + private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config, OsuGame osu) { dimLevel = config.GetBindable(OsuConfig.DimLevel); mouseWheelDisabled = config.GetBindable(OsuConfig.MouseDisableWheel); @@ -76,6 +76,19 @@ namespace osu.Game.Screens.Play if (Beatmap == null) throw new Exception("Beatmap was not loaded"); + + try + { + // Try using the preferred user ruleset + ruleset = osu == null ? Beatmap.BeatmapInfo.Ruleset : osu.Ruleset; + HitRenderer = ruleset.CreateHitRendererWith(Beatmap); + } + catch (BeatmapInvalidForModeException) + { + // Default to the beatmap ruleset + ruleset = Beatmap.BeatmapInfo.Ruleset; + HitRenderer = ruleset.CreateHitRendererWith(Beatmap); + } } catch (Exception e) { @@ -102,12 +115,6 @@ namespace osu.Game.Screens.Play sourceClock.Reset(); }); - ruleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance(); - - // Todo: This should be done as early as possible, and should check if the hit renderer - // can actually convert the hit objects... Somehow... - HitRenderer = ruleset.CreateHitRendererWith(Beatmap); - scoreProcessor = HitRenderer.CreateScoreProcessor(); hudOverlay = new StandardHudOverlay()