diff --git a/appveyor.yml b/appveyor.yml index 15484e4c68..69bc762f4c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ install: - cmd: git submodule update --init --recursive --depth=5 - cmd: choco install resharper-clt -y - cmd: choco install nvika -y - - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe + - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe before_build: - cmd: CodeFileSanity.exe - cmd: nuget restore -verbosity quiet diff --git a/osu-framework b/osu-framework index 0773d895d9..8c4f232694 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 0773d895d9aa0729995cd4a23efc28238e35ceed +Subproject commit 8c4f23269447d9ce21a5dbd3a0fd4f6caae9ab38 diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index bd0cc209b6..d0d623178e 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -14,7 +14,7 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Catch.Tests { - public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue> + public class CatchBeatmapConversionTest : BeatmapConversionTest<TestCatchRuleset, ConvertValue> { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests } } - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter(); + protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); } public struct ConvertValue : IEquatable<ConvertValue> @@ -64,4 +64,8 @@ namespace osu.Game.Rulesets.Catch.Tests => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) && Precision.AlmostEquals(Position, other.Position, conversion_lenience); } + + public class TestCatchRuleset : CatchRuleset + { + } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index bce20520d3..097750d7e0 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs index d13a6bb860..b5cf0e3d1d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseBananaShower.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs index 2b58fcc93c..8a90b48180 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatchStacker.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs index e7f936ca2a..896582bf0a 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override Beatmap CreateBeatmap(Ruleset ruleset) + protected override IBeatmap CreateBeatmap(Ruleset ruleset) { var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs new file mode 100644 index 0000000000..5b4af6ea8a --- /dev/null +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Beatmaps +{ + public class CatchBeatmap : Beatmap<CatchHitObject> + { + public override IEnumerable<BeatmapStatistic> GetStatistics() + { + int fruits = HitObjects.Count(s => s is Fruit); + int juiceStreams = HitObjects.Count(s => s is JuiceStream); + int bananaShowers = HitObjects.Count(s => s is BananaShower); + + return new[] + { + new BeatmapStatistic + { + Name = @"Fruit Count", + Content = fruits.ToString(), + Icon = FontAwesome.fa_circle_o + }, + new BeatmapStatistic + { + Name = @"Juice Stream Count", + Content = juiceStreams.ToString(), + Icon = FontAwesome.fa_circle + }, + new BeatmapStatistic + { + Name = @"Banana Shower Count", + Content = bananaShowers.ToString(), + Icon = FontAwesome.fa_circle + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 34e5f425fd..f40ef67dfb 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -13,9 +13,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject> { + public CatchBeatmapConverter(IBeatmap beatmap) + : base(beatmap) + { + } + protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; - protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap) + protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap) { var curveData = obj as IHasCurve; var positionData = obj as IHasXPosition; @@ -64,5 +69,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps X = positionData.X / CatchPlayfield.BASE_WIDTH }; } + + protected override Beatmap<CatchHitObject> CreateBeatmap() => new CatchBeatmap(); } } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index dfd10e0df7..e16f5fcb60 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -12,16 +12,21 @@ using OpenTK; namespace osu.Game.Rulesets.Catch.Beatmaps { - public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject> + public class CatchBeatmapProcessor : BeatmapProcessor { - public override void PostProcess(Beatmap<CatchHitObject> beatmap) + public CatchBeatmapProcessor(IBeatmap beatmap) + : base(beatmap) { - initialiseHyperDash(beatmap.HitObjects); + } - base.PostProcess(beatmap); + public override void PostProcess() + { + initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects); + + base.PostProcess(); int index = 0; - foreach (var obj in beatmap.HitObjects) + foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>()) obj.IndexInBeatmap = index++; } diff --git a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs index 876b394da0..f47d09fe20 100644 --- a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs @@ -2,20 +2,16 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; using System.Collections.Generic; namespace osu.Game.Rulesets.Catch { - public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject> + public class CatchDifficultyCalculator : DifficultyCalculator { - public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap) + public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap) { } public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0; - - protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); } } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index cfe0fc5cec..15e51fa126 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -13,12 +13,15 @@ using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Catch.Beatmaps; namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new CatchRulesetContainer(this, beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); + public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] { @@ -138,7 +141,7 @@ namespace osu.Game.Rulesets.Catch public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); public override int? LegacyID => 2; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index df7578799f..8e19c0614a 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -6,10 +6,11 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using System; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject> + public class CatchModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.12; public override bool Ranked => true; @@ -17,9 +18,11 @@ namespace osu.Game.Rulesets.Catch.Mods private float lastStartX; private int lastStartTime; - public void ApplyToHitObject(CatchHitObject hitObject) + public void ApplyToHitObject(HitObject hitObject) { - float position = hitObject.X; + var catchObject = (CatchHitObject)hitObject; + + float position = catchObject.X; int startTime = (int)hitObject.StartTime; if (lastStartX == 0) @@ -60,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Mods position += rand; } - hitObject.X = position; + catchObject.X = position; return; } @@ -79,7 +82,7 @@ namespace osu.Game.Rulesets.Catch.Mods } } - hitObject.X = position; + catchObject.X = position; lastStartX = position; lastStartTime = startTime; diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index d63d1bd331..d5c5eb844a 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Replays Dashing = dashing; } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) { Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 022a8a8b43..070dc19a6f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -4,7 +4,6 @@ using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; -using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Replays; @@ -20,8 +19,8 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject> { - public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } @@ -29,10 +28,6 @@ namespace osu.Game.Rulesets.Catch.UI protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); - protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor(); - - protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter(); - protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 81c537e53c..f1ee874b88 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -14,17 +14,14 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Mania.Tests { - public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue> + public class ManiaBeatmapConversionTest : BeatmapConversionTest<TestManiaRuleset, ConvertValue> { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; - private bool isForCurrentRuleset; - [NonParallelizable] - [TestCase("basic", false)] - public void Test(string name, bool isForCurrentRuleset) + [TestCase("basic")] + public new void Test(string name) { - this.isForCurrentRuleset = isForCurrentRuleset; base.Test(name); } @@ -38,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests }; } - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap); + protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); } public struct ConvertValue : IEquatable<ConvertValue> @@ -57,4 +54,8 @@ namespace osu.Game.Rulesets.Mania.Tests && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) && Column == other.Column; } + + public class TestManiaRuleset : ManiaRuleset + { + } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 6af3719f83..ad5f8e447d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; @@ -29,5 +30,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { Stages.Add(defaultStage); } + + public override IEnumerable<BeatmapStatistic> GetStatistics() + { + int notes = HitObjects.Count(s => s is Note); + int holdnotes = HitObjects.Count(s => s is HoldNote); + + return new[] + { + new BeatmapStatistic + { + Name = @"Note Count", + Content = notes.ToString(), + Icon = FontAwesome.fa_circle_o + }, + new BeatmapStatistic + { + Name = @"Hold Note Count", + Content = holdnotes.ToString(), + Icon = FontAwesome.fa_circle + }, + }; + } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 60b92cb7b3..c8a7402904 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -33,18 +33,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private ManiaBeatmap beatmap; - public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original) + public ManiaBeatmapConverter(IBeatmap beatmap) + : base(beatmap) { - IsForCurrentRuleset = isForCurrentRuleset; + IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo); - var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize); - var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty); + var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize); + var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); - if (isForCurrentRuleset) + if (IsForCurrentRuleset) TargetColumns = (int)Math.Max(1, roundedCircleSize); else { - float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count; + float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count(); if (percentSliderOrSpinner < 0.2) TargetColumns = 7; else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) @@ -56,8 +57,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original) + protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original) { + BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); @@ -68,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); - protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) + protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap) { var maniaOriginal = original as ManiaHitObject; if (maniaOriginal != null) @@ -112,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// <param name="original">The original hit object.</param> /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param> /// <returns>The hit objects generated.</returns> - private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap) + private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap) { var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); @@ -128,7 +130,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// <param name="original">The original hit object.</param> /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param> /// <returns>The hit objects generated.</returns> - private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap) + private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap) { var endTimeData = original as IHasEndTime; var distanceData = original as IHasDistance; @@ -165,7 +167,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// </summary> private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 3b5c028bfd..afa9bdbbd7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 743e230cb2..3f34afee85 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { private readonly double endTime; - public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap) + public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) { var endtimeData = HitObject as IHasEndTime; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 652c92dd78..cec3e18ad6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap) + public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 02306846a3..930597c1ad 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// <summary> /// The beatmap which <see cref="HitObject"/> is being converted from. /// </summary> - protected readonly Beatmap OriginalBeatmap; + protected readonly IBeatmap OriginalBeatmap; - protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) + protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(hitObject, beatmap, previousPattern) { if (random == null) throw new ArgumentNullException(nameof(random)); @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy drainTime /= 1000; BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; - conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); return conversionDifficulty.Value; diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index 5eea346836..822ba53eeb 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.Mania { - internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject> + internal class ManiaDifficultyCalculator : DifficultyCalculator { private const double star_scaling_factor = 0.018; @@ -31,12 +31,12 @@ namespace osu.Game.Rulesets.Mania /// </summary> private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>(); - public ManiaDifficultyCalculator(Beatmap beatmap) + public ManiaDifficultyCalculator(IBeatmap beatmap) : base(beatmap) { } - public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) + public ManiaDifficultyCalculator(IBeatmap beatmap, Mod[] mods) : base(beatmap, mods) { } @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7; foreach (var hitObject in Beatmap.HitObjects) - difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); + difficultyHitObjects.Add(new ManiaHitObjectDifficulty((ManiaHitObject)hitObject, columnCount)); // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); @@ -140,7 +140,5 @@ namespace osu.Game.Rulesets.Mania return difficulty; } - - protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap); } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0546cbc174..f1d65f855b 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -15,12 +15,14 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Mania.Beatmaps; namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) { @@ -182,7 +184,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); + public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); public override int? LegacyID => 3; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs index dbd30121a8..e02db68a28 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs @@ -3,19 +3,18 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject> + public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter { public override string ShortenedName => Name; public abstract int KeyCount { get; } public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier public override bool Ranked => true; - public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter) + public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter) { var mbc = (ManiaBeatmapConverter)beatmapConverter; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs index 197b37b3f5..7f3985b26d 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs @@ -11,19 +11,23 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject> + public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject> { public override string Name => "Dual Stages"; public override string ShortenedName => "DS"; public override string Description => @"Double the stages, double the fun!"; public override double ScoreMultiplier => 0; - public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter) + private bool isForCurrentRuleset; + + public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter) { var mbc = (ManiaBeatmapConverter)beatmapConverter; + isForCurrentRuleset = mbc.IsForCurrentRuleset; + // Although this can work, for now let's not allow keymods for mania-specific beatmaps - if (mbc.IsForCurrentRuleset) + if (isForCurrentRuleset) return; mbc.TargetColumns *= 2; @@ -34,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Mods var mrc = (ManiaRulesetContainer)rulesetContainer; // Although this can work, for now let's not allow keymods for mania-specific beatmaps - if (mrc.IsForCurrentRuleset) + if (isForCurrentRuleset) return; var newDefinitions = new List<StageDefinition>(); diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 8d86325dd9..bc9fd6e06f 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) { // We don't need to fully convert, just create the converter - var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap); + var converter = new ManiaBeatmapConverter(beatmap); // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 76afaf270f..7123aab901 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Mania.UI public IEnumerable<BarLine> BarLines; - public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; @@ -85,8 +85,6 @@ namespace osu.Game.Rulesets.Mania.UI public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); - protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap); - protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h) { ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 6ac3c016a0..aa7de4ed01 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -15,7 +15,7 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Tests { - public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue> + public class OsuBeatmapConversionTest : BeatmapConversionTest<TestOsuRuleset, ConvertValue> { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Tests }; } - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter(); + protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); } public struct ConvertValue : IEquatable<ConvertValue> @@ -67,4 +67,8 @@ namespace osu.Game.Rulesets.Osu.Tests && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); } + + public class TestOsuRuleset : OsuRuleset + { + } } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs new file mode 100644 index 0000000000..6d90c2a875 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Beatmaps +{ + public class OsuBeatmap : Beatmap<OsuHitObject> + { + public override IEnumerable<BeatmapStatistic> GetStatistics() + { + int circles = HitObjects.Count(c => c is HitCircle); + int sliders = HitObjects.Count(s => s is Slider); + int spinners = HitObjects.Count(s => s is Spinner); + + return new[] + { + new BeatmapStatistic + { + Name = @"Circle Count", + Content = circles.ToString(), + Icon = FontAwesome.fa_circle_o + }, + new BeatmapStatistic + { + Name = @"Slider Count", + Content = sliders.ToString(), + Icon = FontAwesome.fa_circle + }, + new BeatmapStatistic + { + Name = @"Spinner Count", + Content = spinners.ToString(), + Icon = FontAwesome.fa_circle + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 1236076f48..5f3a909488 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -14,9 +14,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject> { + public OsuBeatmapConverter(IBeatmap beatmap) + : base(beatmap) + { + } + protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) }; - protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) + protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap) { var curveData = original as IHasCurve; var endTimeData = original as IHasEndTime; @@ -60,5 +65,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps }; } } + + protected override Beatmap<OsuHitObject> CreateBeatmap() => new OsuBeatmap(); } } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index afa2437bf6..c7c9f4a01a 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -8,12 +8,17 @@ using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Beatmaps { - internal class OsuBeatmapProcessor : BeatmapProcessor<OsuHitObject> + internal class OsuBeatmapProcessor : BeatmapProcessor { - public override void PostProcess(Beatmap<OsuHitObject> beatmap) + public OsuBeatmapProcessor(IBeatmap beatmap) + : base(beatmap) { - applyStacking(beatmap); - base.PostProcess(beatmap); + } + + public override void PostProcess() + { + applyStacking((Beatmap<OsuHitObject>)Beatmap); + base.PostProcess(); } private void applyStacking(Beatmap<OsuHitObject> beatmap) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index 8d4c342740..ea33ec9ae0 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditRulesetContainer : OsuRulesetContainer { - public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 7bf0651443..dce1fc2851 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); + protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap); protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[] { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index cf71116d47..7a30e6b134 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -5,20 +5,23 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject> + public class OsuModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.06; public override bool Ranked => true; - public void ApplyToHitObject(OsuHitObject hitObject) + public void ApplyToHitObject(HitObject hitObject) { - hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y); + var osuObject = (OsuHitObject)hitObject; + + osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); var slider = hitObject as Slider; if (slider == null) diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs index 926a7975f3..4853cd66cd 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -5,36 +5,30 @@ using System; using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; using osu.Game.Rulesets.Osu.OsuDifficulty.Skills; namespace osu.Game.Rulesets.Osu.OsuDifficulty { - public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject> + public class OsuDifficultyCalculator : DifficultyCalculator { private const int section_length = 400; private const double difficulty_multiplier = 0.0675; - public OsuDifficultyCalculator(Beatmap beatmap) + public OsuDifficultyCalculator(IBeatmap beatmap) : base(beatmap) { } - public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods) + public OsuDifficultyCalculator(IBeatmap beatmap, Mod[] mods) : base(beatmap, mods) { } - protected override void PreprocessHitObjects() - { - new OsuBeatmapProcessor().PostProcess(Beatmap); - } - public override double Calculate(Dictionary<string, double> categoryDifficulty = null) { - OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); + OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate); Skill[] skills = { new Aim(), @@ -72,7 +66,5 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty return starRating; } - - protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter(); } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index e0ecee97a3..69a54fb533 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.OsuDifficulty; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using System.Collections.Generic; -using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; @@ -17,17 +16,18 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Osu.Beatmaps; namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new OsuRulesetContainer(this, beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); + public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] { @@ -37,36 +37,6 @@ namespace osu.Game.Rulesets.Osu new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), }; - public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) - { - IEnumerable<HitObject> hitObjects = beatmap.Beatmap.HitObjects; - IEnumerable<HitObject> circles = hitObjects.Where(c => !(c is IHasEndTime)); - IEnumerable<HitObject> sliders = hitObjects.Where(s => s is IHasCurve); - IEnumerable<HitObject> spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve)); - - return new[] - { - new BeatmapStatistic - { - Name = @"Circle Count", - Content = circles.Count().ToString(), - Icon = FontAwesome.fa_circle_o - }, - new BeatmapStatistic - { - Name = @"Slider Count", - Content = sliders.Count().ToString(), - Icon = FontAwesome.fa_circle - }, - new BeatmapStatistic - { - Name = @"Spinner Count", - Content = spinners.Count().ToString(), - Icon = FontAwesome.fa_circle - } - }; - } - public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) { if (mods.HasFlag(LegacyMods.Nightcore)) @@ -181,9 +151,9 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); + public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); - public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); + public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 6f2512cc33..4412b6efab 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) { Position = legacyFrame.Position; if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 8f0feca207..6b9214d9dc 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -6,14 +6,13 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - public class OsuPerformanceCalculator : PerformanceCalculator<OsuHitObject> + public class OsuPerformanceCalculator : PerformanceCalculator { private readonly int countHitCircles; private readonly int beatmapMaxCombo; @@ -27,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Scoring private int count50; private int countMiss; - public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + public OsuPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score) : base(ruleset, beatmap, score) { countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); - beatmapMaxCombo = Beatmap.HitObjects.Count; + beatmapMaxCombo = Beatmap.HitObjects.Count(); beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count) + 1; } @@ -193,7 +192,5 @@ namespace osu.Game.Rulesets.Osu.Scoring private double totalHits => count300 + count100 + count50 + countMiss; private double totalSuccessfulHits => count300 + count100 + count50; - - protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter(); } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 22c7b719cd..ad1052f86a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -7,7 +7,6 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; @@ -21,17 +20,13 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuRulesetContainer : RulesetContainer<OsuHitObject> { - public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); - protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter(); - - protected override BeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor(); - protected override Playfield CreatePlayfield() => new OsuPlayfield(); public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index aa61f2d60b..33a5e1772e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -14,18 +14,15 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Taiko.Tests { - public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue> + public class TaikoBeatmapConversionTest : BeatmapConversionTest<TestTaikoRuleset, ConvertValue> { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - private bool isForCurrentRuleset; - [NonParallelizable] [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] [TestCase("slider-generating-drumroll", false)] - public void Test(string name, bool isForCurrentRuleset) + public new void Test(string name) { - this.isForCurrentRuleset = isForCurrentRuleset; base.Test(name); } @@ -43,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests }; } - protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset); + protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); } public struct ConvertValue : IEquatable<ConvertValue> @@ -70,4 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Tests && IsSwell == other.IsSwell && IsStrong == other.IsStrong; } + + public class TestTaikoRuleset : TaikoRuleset + { + } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs index aa7318b863..1bf24a46bc 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests private Container playfieldContainer; [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) + private void load() { AddStep("Hit!", () => addHitJudgement(false)); AddStep("Kiai hit", () => addHitJudgement(true)); @@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Title = @"Sample Beatmap", AuthorString = @"peppy", }, + Ruleset = new TaikoRuleset().RulesetInfo }, ControlPointInfo = controlPointInfo }); @@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests RelativeSizeAxes = Axes.X, Height = 768, Clock = new FramedClock(rateAdjustClock), - Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) } + Children = new[] { rulesetContainer = new TaikoRulesetContainer(new TaikoRuleset(), beatmap) } }); } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs new file mode 100644 index 0000000000..36f6df7869 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Beatmaps +{ + public class TaikoBeatmap : Beatmap<TaikoHitObject> + { + public override IEnumerable<BeatmapStatistic> GetStatistics() + { + int hits = HitObjects.Count(s => s is Hit); + int drumrolls = HitObjects.Count(s => s is DrumRoll); + int swells = HitObjects.Count(s => s is Swell); + + return new[] + { + new BeatmapStatistic + { + Name = @"Hit Count", + Content = hits.ToString(), + Icon = FontAwesome.fa_circle_o + }, + new BeatmapStatistic + { + Name = @"Drumroll Count", + Content = drumrolls.ToString(), + Icon = FontAwesome.fa_circle + }, + new BeatmapStatistic + { + Name = @"Swell Count", + Content = swells.ToString(), + Icon = FontAwesome.fa_circle + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 2f175a9922..d40e8dc643 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -42,12 +42,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) }; - public TaikoBeatmapConverter(bool isForCurrentRuleset) + public TaikoBeatmapConverter(IBeatmap beatmap) + : base(beatmap) { - this.isForCurrentRuleset = isForCurrentRuleset; + isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new TaikoRuleset().RulesetInfo); } - protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original) + protected override Beatmap<TaikoHitObject> ConvertBeatmap(IBeatmap original) { // Rewrite the beatmap info to add the slider velocity multiplier BeatmapInfo info = original.BeatmapInfo.DeepClone(); @@ -70,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps return converted; } - protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap) + protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap) { var distanceData = obj as IHasDistance; var repeatsData = obj as IHasRepeats; @@ -196,5 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } } } + + protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap(); } } diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index e510b34ad7..2177a3cbdc 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap) { if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs index 58661d7881..f14c53f7ae 100644 --- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs @@ -2,14 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; -using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; using System.Collections.Generic; using System; namespace osu.Game.Rulesets.Taiko { - internal class TaikoDifficultyCalculator : DifficultyCalculator<TaikoHitObject> + internal class TaikoDifficultyCalculator : DifficultyCalculator { private const double star_scaling_factor = 0.04125; @@ -30,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko /// </summary> private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>(); - public TaikoDifficultyCalculator(Beatmap beatmap) + public TaikoDifficultyCalculator(IBeatmap beatmap) : base(beatmap) { } @@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko difficultyHitObjects.Clear(); foreach (var hitObject in Beatmap.HitObjects) - difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject)); + difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject)); // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); @@ -132,7 +131,5 @@ namespace osu.Game.Rulesets.Taiko return difficulty; } - - protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true); } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 06a8e44a14..102de5717f 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -13,12 +13,14 @@ using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Taiko.Beatmaps; namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset); + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new TaikoRulesetContainer(this, beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] { @@ -140,7 +142,7 @@ namespace osu.Game.Rulesets.Taiko public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); public override int? LegacyID => 1; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 3d3c6ab2f3..313c205981 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Scoring; @@ -24,8 +23,8 @@ namespace osu.Game.Rulesets.Taiko.UI { public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject> { - public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } @@ -93,8 +92,6 @@ namespace osu.Game.Rulesets.Taiko.UI public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); - protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset); - public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 6453cdbd3e..f60caf2397 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -275,13 +275,13 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID)); Assert.IsTrue(set.Beatmaps.Count > 0); var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); + Assert.IsTrue(beatmap?.HitObjects.Any() == true); beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); + Assert.IsTrue(beatmap?.HitObjects.Any() == true); beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); + Assert.IsTrue(beatmap?.HitObjects.Any() == true); beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; - Assert.IsTrue(beatmap?.HitObjects.Count > 0); + Assert.IsTrue(beatmap?.HitObjects.Any() == true); } private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000) diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 886c1120d4..0d3e08154f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -12,8 +12,12 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; using osu.Game.Tests.Beatmaps; @@ -24,7 +28,7 @@ namespace osu.Game.Tests.Visual { private RulesetStore rulesets; private TestBeatmapInfoWedge infoWedge; - private readonly List<Beatmap> beatmaps = new List<Beatmap>(); + private readonly List<IBeatmap> beatmaps = new List<IBeatmap>(); private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); [BackgroundDependencyLoader] @@ -72,13 +76,23 @@ namespace osu.Game.Tests.Visual selectBeatmap(testBeatmap); + testBeatmapLabels(ruleset); + // TODO: adjust cases once more info is shown for other gamemodes switch (ruleset) { - case OsuRuleset osu: - testOsuBeatmap(osu); + case OsuRuleset _: testInfoLabels(5); break; + case TaikoRuleset _: + testInfoLabels(5); + break; + case CatchRuleset _: + testInfoLabels(5); + break; + case ManiaRuleset _: + testInfoLabels(4); + break; default: testInfoLabels(2); break; @@ -88,7 +102,7 @@ namespace osu.Game.Tests.Visual testNullBeatmap(); } - private void testOsuBeatmap(OsuRuleset ruleset) + private void testBeatmapLabels(Ruleset ruleset) { AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version"); AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title"); @@ -112,7 +126,7 @@ namespace osu.Game.Tests.Visual AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); } - private void selectBeatmap(Beatmap b) + private void selectBeatmap(IBeatmap b) { BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; @@ -134,11 +148,11 @@ namespace osu.Game.Tests.Visual }); } - private Beatmap createTestBeatmap(RulesetInfo ruleset) + private IBeatmap createTestBeatmap(RulesetInfo ruleset) { List<HitObject> objects = new List<HitObject>(); for (double i = 0; i < 50000; i += 1000) - objects.Add(new HitObject { StartTime = i }); + objects.Add(new TestHitObject { StartTime = i }); return new Beatmap { @@ -153,7 +167,8 @@ namespace osu.Game.Tests.Visual }, Ruleset = ruleset, StarDifficulty = 6, - Version = $"{ruleset.ShortName}Version" + Version = $"{ruleset.ShortName}Version", + BaseDifficulty = new BeatmapDifficulty() }, HitObjects = objects }; @@ -163,5 +178,12 @@ namespace osu.Game.Tests.Visual { public new BufferedWedgeInfo Info => base.Info; } + + private class TestHitObject : HitObject, IHasPosition + { + public float X { get; } = 0; + public float Y { get; } = 0; + public Vector2 Position { get; } = Vector2.Zero; + } } } diff --git a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs index a6a6130a8f..442ca1322a 100644 --- a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; using System.Collections.Generic; using NUnit.Framework; @@ -16,8 +15,6 @@ namespace osu.Game.Tests.Visual public TestCaseBreakOverlay() { - Clock = new FramedClock(); - Child = breakOverlay = new BreakOverlay(true); AddStep("2s break", () => startBreak(2000)); diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs index 25f8ba06c4..bb5bf93a69 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; -using osu.Game.Screens.Multiplayer; +using osu.Game.Screens.Multi.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs index 582ab5ecc9..f037d70493 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs @@ -332,7 +332,7 @@ namespace osu.Game.Tests.Visual private readonly Drawable tracker; - public TimingPointVisualiser(Beatmap beatmap, double length) + public TimingPointVisualiser(IBeatmap beatmap, double length) { this.length = length; diff --git a/osu.Game.Tests/Visual/TestCaseReplay.cs b/osu.Game.Tests/Visual/TestCaseReplay.cs index 6ba671c7fc..5bc16fe420 100644 --- a/osu.Game.Tests/Visual/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplay.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here // to simulate setting a replay rather than having the replay already set for us beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo)); + var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap); // We have the replay var replay = dummyRulesetContainer.Replay; diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs index 88059d2dcf..cb1425ca69 100644 --- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs +++ b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; -using osu.Game.Screens.Multiplayer; +using osu.Game.Screens.Multi.Components; using osu.Game.Users; namespace osu.Game.Tests.Visual diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 12a017f68c..84897853d8 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -15,21 +15,27 @@ namespace osu.Game.Beatmaps /// <summary> /// A Beatmap containing converted HitObjects. /// </summary> - public class Beatmap<T> : IJsonSerializable + public class Beatmap<T> : IBeatmap where T : HitObject { - public BeatmapInfo BeatmapInfo = new BeatmapInfo(); - public ControlPointInfo ControlPointInfo = new ControlPointInfo(); - public List<BreakPeriod> Breaks = new List<BreakPeriod>(); + public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"Unknown", + AuthorString = @"Unknown Creator", + }, + Version = @"Normal", + BaseDifficulty = new BeatmapDifficulty() + }; [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; - /// <summary> - /// The HitObjects this Beatmap contains. - /// </summary> - [JsonConverter(typeof(TypedListConverter<HitObject>))] - public List<T> HitObjects = new List<T>(); + public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo(); + + public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>(); /// <summary> /// Total amount of break time in the beatmap. @@ -38,51 +44,28 @@ namespace osu.Game.Beatmaps public double TotalBreakTime => Breaks.Sum(b => b.Duration); /// <summary> - /// Constructs a new beatmap. + /// The HitObjects this Beatmap contains. /// </summary> - /// <param name="original">The original beatmap to use the parameters of.</param> - public Beatmap(Beatmap<T> original = null) - { - BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; - ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; - Breaks = original?.Breaks ?? Breaks; - HitObjects = original?.HitObjects ?? HitObjects; + [JsonConverter(typeof(TypedListConverter<HitObject>))] + public List<T> HitObjects = new List<T>(); - if (original == null && Metadata == null) - { - // we may have no metadata in cases we weren't sourced from the database. - // let's fill it (and other related fields) so we don't need to null-check it in future usages. - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = @"Unknown", - Title = @"Unknown", - AuthorString = @"Unknown Creator", - }, - Version = @"Normal", - BaseDifficulty = new BeatmapDifficulty() - }; - } + IEnumerable<HitObject> IBeatmap.HitObjects => HitObjects; + + public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>(); + + IBeatmap IBeatmap.Clone() => Clone(); + + public Beatmap<T> Clone() + { + var newInstance = (Beatmap<T>)MemberwiseClone(); + newInstance.BeatmapInfo = BeatmapInfo.DeepClone(); + + return newInstance; } } - /// <summary> - /// A Beatmap containing un-converted HitObjects. - /// </summary> public class Beatmap : Beatmap<HitObject> { - /// <summary> - /// Constructs a new beatmap. - /// </summary> - /// <param name="original">The original beatmap to use the parameters of.</param> - public Beatmap(Beatmap original) - : base(original) - { - } - - public Beatmap() - { - } + public Beatmap Clone() => (Beatmap)base.Clone(); } } diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 153cace187..b7a454460f 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -22,32 +22,34 @@ namespace osu.Game.Beatmaps remove => ObjectConverted -= value; } - /// <summary> - /// Checks if a Beatmap can be converted using this Beatmap Converter. - /// </summary> - /// <param name="beatmap">The Beatmap to check.</param> - /// <returns>Whether the Beatmap can be converted using this Beatmap Converter.</returns> - public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType)); + public IBeatmap Beatmap { get; } - /// <summary> - /// Converts a Beatmap using this Beatmap Converter. - /// </summary> - /// <param name="original">The un-converted Beatmap.</param> - /// <returns>The converted Beatmap.</returns> - public Beatmap<T> Convert(Beatmap original) + protected BeatmapConverter(IBeatmap beatmap) { - // We always operate on a clone of the original beatmap, to not modify it game-wide - return ConvertBeatmap(new Beatmap(original)); + Beatmap = beatmap; } - void IBeatmapConverter.Convert(Beatmap original) => Convert(original); + /// <summary> + /// Whether <see cref="Beatmap"/> can be converted by this <see cref="BeatmapConverter{T}"/>. + /// </summary> + public bool CanConvert => !Beatmap.HitObjects.Any() || ValidConversionTypes.All(t => Beatmap.HitObjects.Any(t.IsInstanceOfType)); + + /// <summary> + /// Converts <see cref="Beatmap"/>. + /// </summary> + /// <returns>The converted Beatmap.</returns> + public IBeatmap Convert() + { + // We always operate on a clone of the original beatmap, to not modify it game-wide + return ConvertBeatmap(Beatmap.Clone()); + } /// <summary> /// Performs the conversion of a Beatmap using this Beatmap Converter. /// </summary> /// <param name="original">The un-converted Beatmap.</param> /// <returns>The converted Beatmap.</returns> - protected virtual Beatmap<T> ConvertBeatmap(Beatmap original) + protected virtual Beatmap<T> ConvertBeatmap(IBeatmap original) { var beatmap = CreateBeatmap(); @@ -67,7 +69,7 @@ namespace osu.Game.Beatmaps /// <param name="original">The hit object to convert.</param> /// <param name="beatmap">The un-converted Beatmap.</param> /// <returns>The converted hit object.</returns> - private IEnumerable<T> convert(HitObject original, Beatmap beatmap) + private IEnumerable<T> convert(HitObject original, IBeatmap beatmap) { // Check if the hitobject is already the converted type T tObject = original as T; @@ -107,6 +109,6 @@ namespace osu.Game.Beatmaps /// <param name="original">The hit object to convert.</param> /// <param name="beatmap">The un-converted Beatmap.</param> /// <returns>The converted hit object.</returns> - protected abstract IEnumerable<T> ConvertHitObject(HitObject original, Beatmap beatmap); + protected abstract IEnumerable<T> ConvertHitObject(HitObject original, IBeatmap beatmap); } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 645e52a6c6..36fde8a319 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -9,7 +9,9 @@ using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Extensions; +using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps.Formats; @@ -333,7 +335,7 @@ namespace osu.Game.Beatmaps ms.Position = 0; var decoder = Decoder.GetDecoder<Beatmap>(sr); - Beatmap beatmap = decoder.Decode(sr); + IBeatmap beatmap = decoder.Decode(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); @@ -341,9 +343,16 @@ namespace osu.Game.Beatmaps RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); - // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.Ruleset = ruleset; - beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; + + if (ruleset != null) + { + // TODO: this should be done in a better place once we actually need to dynamically update it. + var converted = new DummyConversionBeatmap(beatmap).GetPlayableBeatmap(ruleset); + beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate(); + } + else + beatmap.BeatmapInfo.StarDifficulty = 0; beatmapInfos.Add(beatmap.BeatmapInfo); } @@ -351,5 +360,23 @@ namespace osu.Game.Beatmaps return beatmapInfos; } + + /// <summary> + /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. + /// </summary> + private class DummyConversionBeatmap : WorkingBeatmap + { + private readonly IBeatmap beatmap; + + public DummyConversionBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + protected override Texture GetBackground() => null; + protected override Track GetTrack() => null; + } } } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 8e09d66c42..71406c6034 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps this.audioManager = audioManager; } - protected override Beatmap GetBeatmap() + protected override IBeatmap GetBeatmap() { try { diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 8f5a2a4cab..bf1cd7d4ee 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -2,30 +2,47 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { + public interface IBeatmapProcessor + { + IBeatmap Beatmap { get; } + + /// <summary> + /// Post-processes <see cref="Beatmap"/> to add mode-specific components that aren't added during conversion. + /// <para> + /// An example of such a usage is for combo colours. + /// </para> + /// </summary> + void PostProcess(); + } + /// <summary> /// Processes a post-converted Beatmap. /// </summary> /// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam> - public class BeatmapProcessor<TObject> - where TObject : HitObject + public class BeatmapProcessor : IBeatmapProcessor { + public IBeatmap Beatmap { get; } + + public BeatmapProcessor(IBeatmap beatmap) + { + Beatmap = beatmap; + } + /// <summary> /// Post-processes a Beatmap to add mode-specific components that aren't added during conversion. /// <para> /// An example of such a usage is for combo colours. /// </para> /// </summary> - /// <param name="beatmap">The Beatmap to process.</param> - public virtual void PostProcess(Beatmap<TObject> beatmap) + public virtual void PostProcess() { IHasComboInformation lastObj = null; - foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>()) + foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>()) { if (obj.NewCombo) { diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 5e2d9afd23..37155c09cd 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects; using System.Collections.Generic; using osu.Game.Rulesets.Mods; using osu.Framework.Timing; @@ -12,30 +11,17 @@ namespace osu.Game.Beatmaps { public abstract class DifficultyCalculator { - protected double TimeRate = 1; - - public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null); - } - - public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject - { - protected readonly Beatmap<T> Beatmap; + protected readonly IBeatmap Beatmap; protected readonly Mod[] Mods; - protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null) + protected double TimeRate = 1; + + protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) { + Beatmap = beatmap; Mods = mods ?? new Mod[0]; - var converter = CreateBeatmapConverter(beatmap); - - foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<T>>()) - mod.ApplyToBeatmapConverter(converter); - - Beatmap = converter.Convert(beatmap); - ApplyMods(Mods); - - PreprocessHitObjects(); } protected virtual void ApplyMods(Mod[] mods) @@ -43,22 +29,12 @@ namespace osu.Game.Beatmaps var clock = new StopwatchClock(); mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock)); TimeRate = clock.Rate; - - foreach (var mod in Mods.OfType<IApplicableToDifficulty>()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); - - foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); - - foreach (var mod in mods.OfType<IApplicableToHitObject<T>>()) - foreach (var obj in Beatmap.HitObjects) - mod.ApplyToHitObject(obj); } protected virtual void PreprocessHitObjects() { } - protected abstract BeatmapConverter<T> CreateBeatmapConverter(Beatmap beatmap); + public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null); } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0424ff84f1..da52dc7284 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -7,6 +7,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; namespace osu.Game.Beatmaps @@ -39,7 +40,7 @@ namespace osu.Game.Beatmaps this.game = game; } - protected override Beatmap GetBeatmap() => new Beatmap(); + protected override IBeatmap GetBeatmap() => new Beatmap(); protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4"); @@ -53,12 +54,14 @@ namespace osu.Game.Beatmaps { public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { }; - public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) + public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) { throw new NotImplementedException(); } - public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null; + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap }; + + public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => null; public override string Description => "dummy"; @@ -68,6 +71,14 @@ namespace osu.Game.Beatmaps : base(rulesetInfo) { } + + private class DummyBeatmapConverter : IBeatmapConverter + { + public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted; + public IBeatmap Beatmap { get; set; } + public bool CanConvert => true; + public IBeatmap Convert() => Beatmap; + } } } } diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs new file mode 100644 index 0000000000..fe20bce98a --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -0,0 +1,56 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.IO.Serialization; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Beatmaps +{ + public interface IBeatmap : IJsonSerializable + { + /// <summary> + /// This beatmap's info. + /// </summary> + BeatmapInfo BeatmapInfo { get; set; } + + /// <summary> + /// This beatmap's metadata. + /// </summary> + BeatmapMetadata Metadata { get; } + + /// <summary> + /// The control points in this beatmap. + /// </summary> + ControlPointInfo ControlPointInfo { get; } + + /// <summary> + /// The breaks in this beatmap. + /// </summary> + List<BreakPeriod> Breaks { get; } + + /// <summary> + /// Total amount of break time in the beatmap. + /// </summary> + double TotalBreakTime { get; } + + /// <summary> + /// The hitobjects contained by this beatmap. + /// </summary> + IEnumerable<HitObject> HitObjects { get; } + + /// <summary> + /// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap. + /// </summary> + /// <returns></returns> + IEnumerable<BeatmapStatistic> GetStatistics(); + + /// <summary> + /// Creates a shallow-clone of this beatmap and returns it. + /// </summary> + /// <returns>The shallow-cloned beatmap.</returns> + IBeatmap Clone(); + } +} diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs index 6c25395a56..00566093b8 100644 --- a/osu.Game/Beatmaps/IBeatmapConverter.cs +++ b/osu.Game/Beatmaps/IBeatmapConverter.cs @@ -16,10 +16,16 @@ namespace osu.Game.Beatmaps /// </summary> event Action<HitObject, IEnumerable<HitObject>> ObjectConverted; + IBeatmap Beatmap { get; } + /// <summary> - /// Converts a Beatmap using this Beatmap Converter. + /// Whether <see cref="Beatmap"/> can be converted by this <see cref="IBeatmapConverter"/>. /// </summary> - /// <param name="beatmap">The un-converted Beatmap.</param> - void Convert(Beatmap beatmap); + bool CanConvert { get; } + + /// <summary> + /// Converts <see cref="Beatmap"/>. + /// </summary> + IBeatmap Convert(); } } diff --git a/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs b/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs deleted file mode 100644 index eea82dac6d..0000000000 --- a/osu.Game/Beatmaps/Legacy/LegacyBeatmap.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.Legacy -{ - /// <summary> - /// A type of Beatmap loaded from a legacy .osu beatmap file (version <=15). - /// </summary> - public class LegacyBeatmap : Beatmap - { - /// <summary> - /// Constructs a new beatmap. - /// </summary> - /// <param name="original">The original beatmap to use the parameters of.</param> - internal LegacyBeatmap(Beatmap original = null) - : base(original) - { - HitObjects = original?.HitObjects; - } - } -} diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 4080e34e81..9c389bbb8f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,6 +14,8 @@ using osu.Framework.IO.File; using System.IO; using osu.Game.IO.Serialization; using System.Diagnostics; +using osu.Game.Rulesets; +using osu.Game.Rulesets.UI; using osu.Game.Skinning; namespace osu.Game.Beatmaps @@ -36,7 +38,7 @@ namespace osu.Game.Beatmaps Mods.ValueChanged += mods => applyRateAdjustments(); - beatmap = new AsyncLazy<Beatmap>(populateBeatmap); + beatmap = new AsyncLazy<IBeatmap>(populateBeatmap); background = new AsyncLazy<Texture>(populateBackground, b => b == null || !b.IsDisposed); track = new AsyncLazy<Track>(populateTrack); waveform = new AsyncLazy<Waveform>(populateWaveform); @@ -45,7 +47,7 @@ namespace osu.Game.Beatmaps } /// <summary> - /// Saves the <see cref="Beatmap"/>. + /// Saves the <see cref="Beatmaps.Beatmap"/>. /// </summary> public void Save() { @@ -55,7 +57,7 @@ namespace osu.Game.Beatmaps Process.Start(path); } - protected abstract Beatmap GetBeatmap(); + protected abstract IBeatmap GetBeatmap(); protected abstract Texture GetBackground(); protected abstract Track GetTrack(); protected virtual Skin GetSkin() => new DefaultSkin(); @@ -63,12 +65,11 @@ namespace osu.Game.Beatmaps protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; public bool BeatmapLoaded => beatmap.IsResultAvailable; - public Beatmap Beatmap => beatmap.Value.Result; - public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value; + public IBeatmap Beatmap => beatmap.Value.Result; + public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value; + private readonly AsyncLazy<IBeatmap> beatmap; - private readonly AsyncLazy<Beatmap> beatmap; - - private Beatmap populateBeatmap() + private IBeatmap populateBeatmap() { var b = GetBeatmap() ?? new Beatmap(); @@ -78,6 +79,51 @@ namespace osu.Game.Beatmaps return b; } + /// <summary> + /// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>. + /// <para> + /// The returned <see cref="IBeatmap"/> is in a playable state - all <see cref="HitObject"/> and <see cref="BeatmapDifficulty"/> <see cref="Mod"/>s + /// have been applied, and <see cref="HitObject"/>s have been fully constructed. + /// </para> + /// </summary> + /// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param> + /// <returns>The converted <see cref="IBeatmap"/>.</returns> + /// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception> + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset) + { + var rulesetInstance = ruleset.CreateInstance(); + + IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap); + + // Check if the beatmap can be converted + if (!converter.CanConvert) + throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); + + // Apply conversion mods + foreach (var mod in Mods.Value.OfType<IApplicableToBeatmapConverter>()) + mod.ApplyToBeatmapConverter(converter); + + // Convert + IBeatmap converted = converter.Convert(); + + // Apply difficulty mods + foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>()) + mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); + + // Post-process + rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess(); + + // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed + foreach (var obj in converted.HitObjects) + obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); + + foreach (var mod in Mods.Value.OfType<IApplicableToHitObject>()) + foreach (var obj in converted.HitObjects) + mod.ApplyToHitObject(obj); + + return converted; + } + public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value.Result; public async Task<Texture> GetBackgroundAsync() => await background.Value; diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index 3f59eeca97..f5017de639 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -14,14 +14,18 @@ namespace osu.Game.Graphics.UserInterface public class BreadcrumbControl<T> : OsuTabControl<T> { private const float padding = 10; + private const float item_chevron_size = 10; - protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value); + protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value) + { + AccentColour = AccentColour, + }; - protected override float StripWidth() => base.StripWidth() - (padding + 8); + protected override float StripWidth() => base.StripWidth() - (padding + item_chevron_size); public BreadcrumbControl() { - Height = 26; + Height = 32; TabContainer.Spacing = new Vector2(padding, 0f); Current.ValueChanged += tab => { @@ -47,6 +51,7 @@ namespace osu.Game.Graphics.UserInterface public override bool HandleKeyboardInput => State == Visibility.Visible; public override bool HandleMouseInput => State == Visibility.Visible; + public override bool IsRemovable => true; private Visibility state; @@ -77,13 +82,14 @@ namespace osu.Game.Graphics.UserInterface public BreadcrumbTabItem(T value) : base(value) { - Text.TextSize = 16; - Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width + Text.TextSize = 18; + Text.Margin = new MarginPadding { Vertical = 8 }; + Padding = new MarginPadding { Right = padding + item_chevron_size }; Add(Chevron = new SpriteIcon { Anchor = Anchor.CentreRight, Origin = Anchor.CentreLeft, - Size = new Vector2(12), + Size = new Vector2(item_chevron_size), Icon = FontAwesome.fa_chevron_right, Margin = new MarginPadding { Left = padding }, Alpha = 0f, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 331dcec4c0..a2542c537f 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -12,10 +12,8 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics; @@ -181,7 +179,7 @@ namespace osu.Game.Overlays { textbox.HoldFocus = false; if (1f - ChatHeight.Value < channel_selection_min_height) - transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint); + this.TransformBindableTo(ChatHeight, 1f - channel_selection_min_height, 800, Easing.OutQuint); } else textbox.HoldFocus = true; @@ -533,26 +531,5 @@ namespace osu.Game.Overlays api.Queue(req); } - - private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None) - { - this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing)); - } - - private class TransformChatHeight : Transform<double, ChatOverlay> - { - private double valueAt(double time) - { - if (time < StartTime) return StartValue; - if (time >= EndTime) return EndValue; - - return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); - } - - public override string TargetMember => "ChatHeight.Value"; - - protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time); - protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value; - } } } diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 8883dfdebb..4f4348c131 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -12,6 +10,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Overlays.SearchableList; using osu.Game.Rulesets; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Overlays.Direct { @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Direct protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; + protected override Drawable CreateSupplementaryControls() { modeButtons = new FillFlowContainer<RulesetToggleButton> @@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Direct { DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - Ruleset.BindTo(game?.Ruleset ?? new Bindable<RulesetInfo> { Value = rulesets.GetRuleset(0) }); + Ruleset.Value = game?.Ruleset.Value ?? rulesets.GetRuleset(0); foreach (var r in rulesets.AvailableRulesets) { modeButtons.Add(new RulesetToggleButton(Ruleset, r)); @@ -49,14 +50,15 @@ namespace osu.Game.Overlays.Direct { private Drawable icon { - get { return iconContainer.Icon; } - set { iconContainer.Icon = value; } + get => iconContainer.Icon; + set => iconContainer.Icon = value; } private RulesetInfo ruleset; + public RulesetInfo Ruleset { - get { return ruleset; } + get => ruleset; set { ruleset = value; @@ -73,6 +75,9 @@ namespace osu.Game.Overlays.Direct iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); } + public override bool HandleKeyboardInput => !bindable.Disabled && base.HandleKeyboardInput; + public override bool HandleMouseInput => !bindable.Disabled && base.HandleMouseInput; + public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset) { this.bindable = bindable; diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 05866f7002..1da51e4a5a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -87,8 +87,8 @@ namespace osu.Game.Overlays.Toolbar ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); } - public override bool HandleKeyboardInput => !ruleset.Disabled; - public override bool HandleMouseInput => !ruleset.Disabled; + public override bool HandleKeyboardInput => !ruleset.Disabled && base.HandleKeyboardInput; + public override bool HandleMouseInput => !ruleset.Disabled && base.HandleMouseInput; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1820053d3d..5f1b9a6bad 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Edit private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; - protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap); protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; } diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs index a03a003810..1b8e62b53c 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs @@ -10,13 +10,12 @@ namespace osu.Game.Rulesets.Mods /// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>. /// </summary> /// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam> - public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod - where TObject : HitObject + public interface IApplicableToBeatmapConverter : IApplicableMod { /// <summary> /// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>. /// </summary> /// <param name="beatmapConverter">The <see cref="BeatmapConverter{TObject}"/> to apply to.</param> - void ApplyToBeatmapConverter(BeatmapConverter<TObject> beatmapConverter); + void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter); } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs index 0fd2e398c8..d6f330d9df 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs @@ -8,13 +8,12 @@ namespace osu.Game.Rulesets.Mods /// <summary> /// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s. /// </summary> - public interface IApplicableToHitObject<in TObject> : IApplicableMod - where TObject : HitObject + public interface IApplicableToHitObject : IApplicableMod { /// <summary> /// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>. /// </summary> /// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param> - void ApplyToHitObject(TObject hitObject); + void ApplyToHitObject(HitObject hitObject); } } diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index adf35ce078..fdd528f296 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Replays.Types /// </summary> /// <param name="legacyFrame">The <see cref="LegacyReplayFrame"/> to extract values from.</param> /// <param name="beatmap">The beatmap.</param> - void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); + void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cd1d030afe..6883d319f4 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets { public readonly RulesetInfo RulesetInfo; - public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; - public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>() // Confine all mods of each mod type into a single IEnumerable<Mod> .SelectMany(GetModsFor) @@ -52,14 +50,17 @@ namespace osu.Game.Rulesets /// Attempt to create a hit renderer for a beatmap /// </summary> /// <param name="beatmap">The beatmap to create the hit renderer for.</param> - /// <param name="isForCurrentRuleset">Whether the hit renderer should assume the beatmap is for the current ruleset.</param> /// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception> /// <returns></returns> - public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); + public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap); - public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); + public abstract IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap); - public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; + public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null; + + public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null); + + public virtual PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => null; public virtual HitObjectComposer CreateHitObjectComposer() => null; @@ -114,7 +115,8 @@ namespace osu.Game.Rulesets Name = Description, ShortName = ShortName, InstantiationInfo = GetType().AssemblyQualifiedName, - ID = LegacyID + ID = LegacyID, + Available = true }; } } diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 239f200e29..d5ab856697 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy this.beatmaps = beatmaps; } - private Beatmap currentBeatmap; + private IBeatmap currentBeatmap; private Ruleset currentRuleset; public Score Parse(Stream stream) diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index 0f115fff6b..5b8f5f0d0f 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -2,42 +2,28 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Scoring { public abstract class PerformanceCalculator - { - public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null); - } - - public abstract class PerformanceCalculator<TObject> : PerformanceCalculator - where TObject : HitObject { private readonly Dictionary<string, double> attributes = new Dictionary<string, double>(); protected IDictionary<string, double> Attributes => attributes; - protected readonly Beatmap<TObject> Beatmap; + protected readonly IBeatmap Beatmap; protected readonly Score Score; - protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score) { Score = score; - var converter = CreateBeatmapConverter(); - - foreach (var mod in score.Mods.OfType<IApplicableToBeatmapConverter<TObject>>()) - mod.ApplyToBeatmapConverter(converter); - - Beatmap = converter.Convert(beatmap); + Beatmap = beatmap; var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); diffCalc.Calculate(attributes); } - protected abstract BeatmapConverter<TObject> CreateBeatmapConverter(); + public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null); } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index d1f1807937..e42e74c245 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -190,11 +190,6 @@ namespace osu.Game.Rulesets.UI /// </summary> protected readonly WorkingBeatmap WorkingBeatmap; - /// <summary> - /// Whether the specified beatmap is assumed to be specific to the current ruleset. - /// </summary> - public readonly bool IsForCurrentRuleset; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this); protected override Container<Drawable> Content => content; @@ -206,43 +201,18 @@ namespace osu.Game.Rulesets.UI /// </summary> /// <param name="ruleset">The ruleset being repesented.</param> /// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param> - /// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param> - protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset) + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap) : base(ruleset) { Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap."); WorkingBeatmap = workingBeatmap; - IsForCurrentRuleset = isForCurrentRuleset; // ReSharper disable once PossibleNullReferenceException Mods = workingBeatmap.Mods.Value; RelativeSizeAxes = Axes.Both; - BeatmapConverter<TObject> converter = CreateBeatmapConverter(); - BeatmapProcessor<TObject> processor = CreateBeatmapProcessor(); - - // Check if the beatmap can be converted - if (!converter.CanConvert(workingBeatmap.Beatmap)) - throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter})."); - - // Apply conversion adjustments before converting - foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<TObject>>()) - mod.ApplyToBeatmapConverter(converter); - - // Convert the beatmap - Beatmap = converter.Convert(workingBeatmap.Beatmap); - - // Apply difficulty adjustments from mods before using Difficulty. - foreach (var mod in Mods.OfType<IApplicableToDifficulty>()) - mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty); - - // Post-process the beatmap - processor.PostProcess(Beatmap); - - // Apply defaults - foreach (var h in Beatmap.HitObjects) - h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo); KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager.RelativeSizeAxes = Axes.Both; @@ -277,10 +247,6 @@ namespace osu.Game.Rulesets.UI if (mods == null) return; - foreach (var mod in mods.OfType<IApplicableToHitObject<TObject>>()) - foreach (var obj in Beatmap.HitObjects) - mod.ApplyToHitObject(obj); - foreach (var mod in mods.OfType<IApplicableToRulesetContainer<TObject>>()) mod.ApplyToRulesetContainer(this); } @@ -324,13 +290,6 @@ namespace osu.Game.Rulesets.UI Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; } - /// <summary> - /// Creates a processor to perform post-processing operations - /// on HitObjects in converted Beatmaps. - /// </summary> - /// <returns>The Beatmap processor.</returns> - protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>(); - /// <summary> /// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments. /// </summary> @@ -344,12 +303,6 @@ namespace osu.Game.Rulesets.UI /// </summary> protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default - /// <summary> - /// Creates a converter to convert Beatmap to a specific mode. - /// </summary> - /// <returns>The Beatmap converter.</returns> - protected abstract BeatmapConverter<TObject> CreateBeatmapConverter(); - /// <summary> /// Creates a DrawableHitObject from a HitObject. /// </summary> @@ -377,9 +330,8 @@ namespace osu.Game.Rulesets.UI /// </summary> /// <param name="ruleset">The ruleset being repesented.</param> /// <param name="beatmap">The beatmap to create the hit renderer for.</param> - /// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param> - protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 1e7f561aba..6f86d20295 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -4,9 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Transforms; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; using OpenTK.Input; @@ -90,10 +88,10 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (args.Key) { case Key.Minus: - transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint); + this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 200, Easing.OutQuint); break; case Key.Plus: - transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint); + this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 200, Easing.OutQuint); break; } } @@ -101,27 +99,6 @@ namespace osu.Game.Rulesets.UI.Scrolling return false; } - private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None) - { - this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing)); - } - protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); - - private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield> - { - private double valueAt(double time) - { - if (time < StartTime) return StartValue; - if (time >= EndTime) return EndValue; - - return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); - } - - public override string TargetMember => "VisibleTimeRange.Value"; - - protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time); - protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value; - } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 79f9eaa9e9..efd901240a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.UI.Scrolling /// <returns></returns> protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default); - protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) - : base(ruleset, beatmap, isForCurrentRuleset) + protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index adb749b492..e4eaee76fc 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -27,6 +27,7 @@ namespace osu.Game.Screens.Edit protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); public override bool ShowOverlaysOnEnter => false; + public override bool AllowBeatmapRulesetChange => false; private Box bottomBackground; private Container screenContainer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index f2ea6d85a8..907ad81111 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -14,7 +14,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; using osu.Game.Screens.Direct; using osu.Game.Screens.Edit; -using osu.Game.Screens.Multiplayer; +using osu.Game.Screens.Multi.Screens; using osu.Game.Screens.Select; using osu.Game.Screens.Tournament; diff --git a/osu.Game/Screens/Multiplayer/DrawableGameType.cs b/osu.Game/Screens/Multi/Components/DrawableGameType.cs similarity index 96% rename from osu.Game/Screens/Multiplayer/DrawableGameType.cs rename to osu.Game/Screens/Multi/Components/DrawableGameType.cs index 5790008f76..3406e179d4 100644 --- a/osu.Game/Screens/Multiplayer/DrawableGameType.cs +++ b/osu.Game/Screens/Multi/Components/DrawableGameType.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Online.Multiplayer; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Components { public class DrawableGameType : CircularContainer, IHasTooltip { diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multi/Components/DrawableRoom.cs similarity index 99% rename from osu.Game/Screens/Multiplayer/DrawableRoom.cs rename to osu.Game/Screens/Multi/Components/DrawableRoom.cs index d53100526f..040fbaf593 100644 --- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Components/DrawableRoom.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; @@ -17,8 +15,10 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Multiplayer; using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Components { public class DrawableRoom : OsuClickableContainer { diff --git a/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs similarity index 98% rename from osu.Game/Screens/Multiplayer/ModeTypeInfo.cs rename to osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index 08e96ba55d..e3aba685a7 100644 --- a/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -1,14 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Online.Multiplayer; +using OpenTK; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Components { public class ModeTypeInfo : Container { diff --git a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs b/osu.Game/Screens/Multi/Components/ParticipantInfo.cs similarity index 99% rename from osu.Game/Screens/Multiplayer/ParticipantInfo.cs rename to osu.Game/Screens/Multi/Components/ParticipantInfo.cs index ff1887fa17..ab404488f1 100644 --- a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Components/ParticipantInfo.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,8 +10,9 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; +using OpenTK; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Components { public class ParticipantInfo : Container { diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multi/Components/RoomInspector.cs similarity index 99% rename from osu.Game/Screens/Multiplayer/RoomInspector.cs rename to osu.Game/Screens/Multi/Components/RoomInspector.cs index bfc4a44ed5..92910e8301 100644 --- a/osu.Game/Screens/Multiplayer/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Components/RoomInspector.cs @@ -2,8 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; @@ -20,8 +18,10 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.Multiplayer; using osu.Game.Users; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Components { public class RoomInspector : Container { diff --git a/osu.Game/Screens/Multiplayer/Lobby.cs b/osu.Game/Screens/Multi/Screens/Lobby.cs similarity index 90% rename from osu.Game/Screens/Multiplayer/Lobby.cs rename to osu.Game/Screens/Multi/Screens/Lobby.cs index 65fa5fbb16..dcda40e0d7 100644 --- a/osu.Game/Screens/Multiplayer/Lobby.cs +++ b/osu.Game/Screens/Multi/Screens/Lobby.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Screens { public class Lobby : ScreenWhiteBox { diff --git a/osu.Game/Screens/Multiplayer/Match.cs b/osu.Game/Screens/Multi/Screens/Match.cs similarity index 96% rename from osu.Game/Screens/Multiplayer/Match.cs rename to osu.Game/Screens/Multi/Screens/Match.cs index 5402e70ea5..4ba7fe9f6a 100644 --- a/osu.Game/Screens/Multiplayer/Match.cs +++ b/osu.Game/Screens/Multi/Screens/Match.cs @@ -3,14 +3,14 @@ using System; using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; -using OpenTK.Graphics; using osu.Game.Screens.Select; -using osu.Framework.Graphics; +using OpenTK.Graphics; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Screens { public class Match : ScreenWhiteBox { diff --git a/osu.Game/Screens/Multiplayer/MatchCreate.cs b/osu.Game/Screens/Multi/Screens/MatchCreate.cs similarity index 91% rename from osu.Game/Screens/Multiplayer/MatchCreate.cs rename to osu.Game/Screens/Multi/Screens/MatchCreate.cs index ca6b814cb9..6b4e26d5e5 100644 --- a/osu.Game/Screens/Multiplayer/MatchCreate.cs +++ b/osu.Game/Screens/Multi/Screens/MatchCreate.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace osu.Game.Screens.Multiplayer +namespace osu.Game.Screens.Multi.Screens { public class MatchCreate : ScreenWhiteBox { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 83958b2912..f397d0c3d4 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Play mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel); userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset); - Beatmap beatmap; + IBeatmap beatmap; try { @@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play try { - RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); + RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working); } catch (BeatmapInvalidForRulesetException) { @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); - RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); + RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap); } if (!RulesetContainer.Objects.Any()) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index f005261ffa..236b1310e1 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -21,6 +23,8 @@ using osu.Game.Rulesets.Objects.Types; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Rulesets; +using osu.Game.Rulesets.UI; namespace osu.Game.Screens.Select { @@ -28,6 +32,8 @@ namespace osu.Game.Screens.Select { private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0); + private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>(); + protected BufferedWedgeInfo Info; public BeatmapInfoWedge() @@ -46,6 +52,14 @@ namespace osu.Game.Screens.Select }; } + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OsuGame osuGame) + { + if (osuGame != null) + ruleset.BindTo(osuGame.Ruleset); + ruleset.ValueChanged += updateRuleset; + } + protected override bool BlockPassThroughMouse => false; protected override void PopIn() @@ -62,9 +76,19 @@ namespace osu.Game.Screens.Select this.FadeOut(500, Easing.In); } + private WorkingBeatmap beatmap; + public void UpdateBeatmap(WorkingBeatmap beatmap) { - LoadComponentAsync(new BufferedWedgeInfo(beatmap) + this.beatmap = beatmap; + loadBeatmap(); + } + + private void updateRuleset(RulesetInfo ruleset) => loadBeatmap(); + + private void loadBeatmap() + { + LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value) { Shear = -Shear, Depth = Info?.Depth + 1 ?? 0, @@ -90,9 +114,13 @@ namespace osu.Game.Screens.Select private UnicodeBindableString titleBinding; private UnicodeBindableString artistBinding; - public BufferedWedgeInfo(WorkingBeatmap working) + private readonly RulesetInfo ruleset; + + public BufferedWedgeInfo(WorkingBeatmap working, RulesetInfo userRuleset) { this.working = working; + + ruleset = userRuleset ?? working.BeatmapInfo.Ruleset; } [BackgroundDependencyLoader] @@ -211,11 +239,10 @@ namespace osu.Game.Screens.Select private InfoLabel[] getInfoLabels() { var beatmap = working.Beatmap; - var info = working.BeatmapInfo; List<InfoLabel> labels = new List<InfoLabel>(); - if (beatmap?.HitObjects?.Count > 0) + if (beatmap?.HitObjects?.Any() == true) { HitObject lastObject = beatmap.HitObjects.LastOrDefault(); double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; @@ -224,7 +251,7 @@ namespace osu.Game.Screens.Select { Name = "Length", Icon = FontAwesome.fa_clock_o, - Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), + Content = TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), })); labels.Add(new InfoLabel(new BeatmapStatistic @@ -234,14 +261,26 @@ namespace osu.Game.Screens.Select Content = getBPMRange(beatmap), })); - //get statistics from the current ruleset. - labels.AddRange(info.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s))); + IBeatmap playableBeatmap; + + try + { + // Try to get the beatmap with the user's ruleset + playableBeatmap = working.GetPlayableBeatmap(ruleset); + } + catch (BeatmapInvalidForRulesetException) + { + // Can't be converted to the user's ruleset, so use the beatmap's own ruleset + playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset); + } + + labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s))); } return labels.ToArray(); } - private string getBPMRange(Beatmap beatmap) + private string getBPMRange(IBeatmap beatmap) { double bpmMax = beatmap.ControlPointInfo.BPMMaximum; double bpmMin = beatmap.ControlPointInfo.BPMMinimum; diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 2850de8ba5..2de38d49a9 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -10,12 +10,14 @@ using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; namespace osu.Game.Tests.Beatmaps { [TestFixture] - public abstract class BeatmapConversionTest<TConvertValue> + public abstract class BeatmapConversionTest<TRuleset, TConvertValue> + where TRuleset : Ruleset, new() where TConvertValue : IEquatable<TConvertValue> { private const string resource_namespace = "Testing.Beatmaps"; @@ -79,6 +81,9 @@ namespace osu.Game.Tests.Beatmaps { var beatmap = getBeatmap(name); + var rulesetInstance = new TRuleset(); + beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); + var result = new ConvertResult(); var converter = CreateConverter(beatmap); @@ -92,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps result.Mappings.Add(mapping); }; - converter.Convert(beatmap); + converter.Convert(); return result; } @@ -107,7 +112,7 @@ namespace osu.Game.Tests.Beatmaps } } - private Beatmap getBeatmap(string name) + private IBeatmap getBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new StreamReader(resStream)) @@ -125,7 +130,7 @@ namespace osu.Game.Tests.Beatmaps } protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject); - protected abstract IBeatmapConverter CreateConverter(Beatmap beatmap); + protected abstract IBeatmapConverter CreateConverter(IBeatmap beatmap); private class ConvertMapping { diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 09a3a7af8c..6bad08baaa 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -12,8 +12,14 @@ namespace osu.Game.Tests.Beatmaps public class TestBeatmap : Beatmap { public TestBeatmap(RulesetInfo ruleset) - : base(createTestBeatmap()) { + var baseBeatmap = createTestBeatmap(); + + BeatmapInfo = baseBeatmap.BeatmapInfo; + ControlPointInfo = baseBeatmap.ControlPointInfo; + Breaks = baseBeatmap.Breaks; + HitObjects = baseBeatmap.HitObjects; + BeatmapInfo.Ruleset = ruleset; } diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index e24fbab3ac..37693c99e8 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -17,14 +17,14 @@ namespace osu.Game.Tests.Beatmaps { } - public TestWorkingBeatmap(Beatmap beatmap) + public TestWorkingBeatmap(IBeatmap beatmap) : base(beatmap.BeatmapInfo) { this.beatmap = beatmap; } - private readonly Beatmap beatmap; - protected override Beatmap GetBeatmap() => beatmap; + private readonly IBeatmap beatmap; + protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetTrack() diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 29132258c2..51460ecb6d 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -259,9 +259,9 @@ namespace osu.Game.Tests.Visual private readonly OsuSpriteText text; private readonly Score score; - private readonly Beatmap beatmap; + private readonly IBeatmap beatmap; - public PerformanceDisplay(Score score, Beatmap beatmap) + public PerformanceDisplay(Score score, IBeatmap beatmap) { this.score = score; this.beatmap = beatmap; diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 5ed43b2814..bda438d906 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual } } - protected virtual Beatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); + protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());