diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 8c9e1f279f..538aaf2d7a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index e45ed8c6f4..493ac7ae39 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.MathUtils; +using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; @@ -22,10 +23,10 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("spinner")] [TestCase("spinner-and-circles")] [TestCase("slider")] - public new void Test(string name) - { - base.Test(name); - } + [TestCase("hardrock-stream", new[] { typeof(CatchModHardRock) })] + [TestCase("hardrock-repeat-slider", new[] { typeof(CatchModHardRock) })] + [TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })] + public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) { diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 645cb5701a..5ab47c1611 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Types; using osuTK; using osu.Game.Rulesets.Catch.MathUtils; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { base.PostProcess(); - applyPositionOffsets(); + ApplyPositionOffsets(Beatmap); initialiseHyperDash((List)Beatmap.HitObjects); @@ -40,19 +41,29 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private void applyPositionOffsets() + public static void ApplyPositionOffsets(IBeatmap beatmap, params Mod[] mods) { var rng = new FastRandom(RNG_SEED); - // todo: HardRock displacement should be applied here - foreach (var obj in Beatmap.HitObjects) + bool shouldApplyHardRockOffset = mods.Any(m => m is ModHardRock); + float? lastPosition = null; + double lastStartTime = 0; + + foreach (var obj in beatmap.HitObjects.OfType()) { + obj.XOffset = 0; + switch (obj) { + case Fruit fruit: + if (shouldApplyHardRockOffset) + applyHardRockOffset(fruit, ref lastPosition, ref lastStartTime, rng); + break; + case BananaShower bananaShower: foreach (var banana in bananaShower.NestedHitObjects.OfType()) { - banana.X = (float)rng.NextDouble(); + banana.XOffset = (float)rng.NextDouble(); rng.Next(); // osu!stable retrieved a random banana type rng.Next(); // osu!stable retrieved a random banana rotation rng.Next(); // osu!stable retrieved a random banana colour @@ -63,12 +74,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps case JuiceStream juiceStream: foreach (var nested in juiceStream.NestedHitObjects) { - var hitObject = (CatchHitObject)nested; - if (hitObject is TinyDroplet) - hitObject.X += rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH; - else if (hitObject is Droplet) + var catchObject = (CatchHitObject)nested; + catchObject.XOffset = 0; + + if (catchObject is TinyDroplet) + catchObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X); + else if (catchObject is Droplet) rng.Next(); // osu!stable retrieved a random droplet rotation - hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1); } break; @@ -76,6 +88,105 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng) + { + if (hitObject is JuiceStream stream) + { + lastPosition = stream.EndX; + lastStartTime = stream.EndTime; + return; + } + + if (!(hitObject is Fruit)) + return; + + float offsetPosition = hitObject.X; + double startTime = hitObject.StartTime; + + if (lastPosition == null) + { + lastPosition = offsetPosition; + lastStartTime = startTime; + + return; + } + + float positionDiff = offsetPosition - lastPosition.Value; + double timeDiff = startTime - lastStartTime; + + if (timeDiff > 1000) + { + lastPosition = offsetPosition; + lastStartTime = startTime; + return; + } + + if (positionDiff == 0) + { + applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng); + hitObject.XOffset = offsetPosition - hitObject.X; + return; + } + + if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) + applyOffset(ref offsetPosition, positionDiff); + + hitObject.XOffset = offsetPosition - hitObject.X; + + lastPosition = offsetPosition; + lastStartTime = startTime; + } + + /// + /// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield. + /// + /// The position which the offset should be applied to. + /// The maximum offset, cannot exceed 20px. + /// The random number generator. + private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng) + { + bool right = rng.NextBool(); + float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH; + + if (right) + { + // Clamp to the right bound + if (position + rand <= 1) + position += rand; + else + position -= rand; + } + else + { + // Clamp to the left bound + if (position - rand >= 0) + position -= rand; + else + position += rand; + } + } + + /// + /// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield. + /// + /// The position which the offset should be applied to. + /// The amount to offset by. + private static void applyOffset(ref float position, float amount) + { + if (amount > 0) + { + // Clamp to the right bound + if (position + amount < 1) + position += amount; + } + else + { + // Clamp to the left bound + if (position + amount > 0) + position += amount; + } + } + private void initialiseHyperDash(List objects) { List objectWithDroplets = new List(); diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs index b3605b013b..c721ff862a 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs @@ -61,6 +61,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + /// + /// Generates a random integer value within the range [, ). + /// + /// The lower bound of the range. + /// The upper bound of the range. + /// The random value. + public int Next(double lowerBound, double upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound)); + /// /// Generates a random double value within the range [0, 1). /// diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index 060e51e31d..ced1900ba9 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -1,121 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; -using System; -using osu.Game.Rulesets.Objects; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModHardRock : ModHardRock, IApplicableToHitObject + public class CatchModHardRock : ModHardRock, IApplicableToBeatmap { public override double ScoreMultiplier => 1.12; public override bool Ranked => true; - private float? lastPosition; - private double lastStartTime; - - public void ApplyToHitObject(HitObject hitObject) - { - if (hitObject is JuiceStream stream) - { - lastPosition = stream.EndX; - lastStartTime = stream.EndTime; - return; - } - - if (!(hitObject is Fruit)) - return; - - var catchObject = (CatchHitObject)hitObject; - - float position = catchObject.X; - double startTime = hitObject.StartTime; - - if (lastPosition == null) - { - lastPosition = position; - lastStartTime = startTime; - - return; - } - - float positionDiff = position - lastPosition.Value; - double timeDiff = startTime - lastStartTime; - - if (timeDiff > 1000) - { - lastPosition = position; - lastStartTime = startTime; - return; - } - - if (positionDiff == 0) - { - applyRandomOffset(ref position, timeDiff / 4d); - catchObject.X = position; - return; - } - - if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d) - applyOffset(ref position, positionDiff); - - catchObject.X = position; - - lastPosition = position; - lastStartTime = startTime; - } - - /// - /// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield. - /// - /// The position which the offset should be applied to. - /// The maximum offset, cannot exceed 20px. - private void applyRandomOffset(ref float position, double maxOffset) - { - bool right = RNG.NextBool(); - float rand = Math.Min(20, (float)RNG.NextDouble(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH; - - if (right) - { - // Clamp to the right bound - if (position + rand <= 1) - position += rand; - else - position -= rand; - } - else - { - // Clamp to the left bound - if (position - rand >= 0) - position -= rand; - else - position += rand; - } - } - - /// - /// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield. - /// - /// The position which the offset should be applied to. - /// The amount to offset by. - private void applyOffset(ref float position, float amount) - { - if (amount > 0) - { - // Clamp to the right bound - if (position + amount < 1) - position += amount; - } - else - { - // Clamp to the left bound - if (position + amount > 0) - position += amount; - } - } + public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index be76edc01b..19a1b59752 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -3,6 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -12,7 +13,18 @@ namespace osu.Game.Rulesets.Catch.Objects { public const double OBJECT_RADIUS = 44; - public float X { get; set; } + private float x; + + public float X + { + get => x + XOffset; + set => x = value; + } + + /// + /// A random offset applied to , set by the . + /// + internal float XOffset { get; set; } public double TimePreempt = 1000; diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json new file mode 100644 index 0000000000..83f9e30800 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json @@ -0,0 +1,150 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 177 + }, + { + "StartTime": 450, + "Position": 216.539276 + }, + { + "StartTime": 532, + "Position": 256.5667 + }, + { + "StartTime": 614, + "Position": 296.594116 + }, + { + "StartTime": 696, + "Position": 336.621521 + }, + { + "StartTime": 778, + "Position": 376.99762 + }, + { + "StartTime": 860, + "Position": 337.318878 + }, + { + "StartTime": 942, + "Position": 297.291443 + }, + { + "StartTime": 1024, + "Position": 257.264038 + }, + { + "StartTime": 1106, + "Position": 217.2366 + }, + { + "StartTime": 1188, + "Position": 177 + }, + { + "StartTime": 1270, + "Position": 216.818192 + }, + { + "StartTime": 1352, + "Position": 256.8456 + }, + { + "StartTime": 1434, + "Position": 296.873047 + }, + { + "StartTime": 1516, + "Position": 336.900452 + }, + { + "StartTime": 1598, + "Position": 376.99762 + }, + { + "StartTime": 1680, + "Position": 337.039948 + }, + { + "StartTime": 1762, + "Position": 297.0125 + }, + { + "StartTime": 1844, + "Position": 256.9851 + }, + { + "StartTime": 1926, + "Position": 216.957672 + }, + { + "StartTime": 2008, + "Position": 177 + }, + { + "StartTime": 2090, + "Position": 217.097137 + }, + { + "StartTime": 2172, + "Position": 257.124573 + }, + { + "StartTime": 2254, + "Position": 297.152 + }, + { + "StartTime": 2336, + "Position": 337.179443 + }, + { + "StartTime": 2418, + "Position": 376.99762 + }, + { + "StartTime": 2500, + "Position": 336.760956 + }, + { + "StartTime": 2582, + "Position": 296.733643 + }, + { + "StartTime": 2664, + "Position": 256.7062 + }, + { + "StartTime": 2746, + "Position": 216.678772 + }, + { + "StartTime": 2828, + "Position": 177 + }, + { + "StartTime": 2909, + "Position": 216.887909 + }, + { + "StartTime": 2991, + "Position": 256.915344 + }, + { + "StartTime": 3073, + "Position": 296.942749 + }, + { + "StartTime": 3155, + "Position": 336.970184 + }, + { + "StartTime": 3237, + "Position": 376.99762 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu new file mode 100644 index 0000000000..c1971edf2c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +177,191,369,6,0,L|382:192,7,200 diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json new file mode 100644 index 0000000000..7333b600fb --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json @@ -0,0 +1,74 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 65 + }, + { + "StartTime": 450, + "Position": 482 + }, + { + "StartTime": 532, + "Position": 164 + }, + { + "StartTime": 614, + "Position": 315 + }, + { + "StartTime": 696, + "Position": 145 + }, + { + "StartTime": 778, + "Position": 159 + }, + { + "StartTime": 860, + "Position": 310 + }, + { + "StartTime": 942, + "Position": 441 + }, + { + "StartTime": 1024, + "Position": 428 + }, + { + "StartTime": 1106, + "Position": 243 + }, + { + "StartTime": 1188, + "Position": 422 + }, + { + "StartTime": 1270, + "Position": 481 + }, + { + "StartTime": 1352, + "Position": 104 + }, + { + "StartTime": 1434, + "Position": 473 + }, + { + "StartTime": 1516, + "Position": 135 + }, + { + "StartTime": 1598, + "Position": 360 + }, + { + "StartTime": 1680, + "Position": 123 + } + ] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu new file mode 100644 index 0000000000..208d21a344 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu @@ -0,0 +1,18 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:4 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +256,192,369,12,0,1680,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json new file mode 100644 index 0000000000..bbc16ab912 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json @@ -0,0 +1,234 @@ +{ + "Mappings": [{ + "StartTime": 369, + "Objects": [{ + "StartTime": 369, + "Position": 258 + }] + }, + { + "StartTime": 450, + "Objects": [{ + "StartTime": 450, + "Position": 254 + }] + }, + { + "StartTime": 532, + "Objects": [{ + "StartTime": 532, + "Position": 241 + }] + }, + { + "StartTime": 614, + "Objects": [{ + "StartTime": 614, + "Position": 238 + }] + }, + { + "StartTime": 696, + "Objects": [{ + "StartTime": 696, + "Position": 238 + }] + }, + { + "StartTime": 778, + "Objects": [{ + "StartTime": 778, + "Position": 278 + }] + }, + { + "StartTime": 860, + "Objects": [{ + "StartTime": 860, + "Position": 238 + }] + }, + { + "StartTime": 942, + "Objects": [{ + "StartTime": 942, + "Position": 278 + }] + }, + { + "StartTime": 1024, + "Objects": [{ + "StartTime": 1024, + "Position": 238 + }] + }, + { + "StartTime": 1106, + "Objects": [{ + "StartTime": 1106, + "Position": 278 + }] + }, + { + "StartTime": 1188, + "Objects": [{ + "StartTime": 1188, + "Position": 278 + }] + }, + { + "StartTime": 1270, + "Objects": [{ + "StartTime": 1270, + "Position": 278 + }] + }, + { + "StartTime": 1352, + "Objects": [{ + "StartTime": 1352, + "Position": 238 + }] + }, + { + "StartTime": 1434, + "Objects": [{ + "StartTime": 1434, + "Position": 258 + }] + }, + { + "StartTime": 1516, + "Objects": [{ + "StartTime": 1516, + "Position": 253 + }] + }, + { + "StartTime": 1598, + "Objects": [{ + "StartTime": 1598, + "Position": 238 + }] + }, + { + "StartTime": 1680, + "Objects": [{ + "StartTime": 1680, + "Position": 260 + }] + }, + { + "StartTime": 1762, + "Objects": [{ + "StartTime": 1762, + "Position": 238 + }] + }, + { + "StartTime": 1844, + "Objects": [{ + "StartTime": 1844, + "Position": 278 + }] + }, + { + "StartTime": 1926, + "Objects": [{ + "StartTime": 1926, + "Position": 278 + }] + }, + { + "StartTime": 2008, + "Objects": [{ + "StartTime": 2008, + "Position": 238 + }] + }, + { + "StartTime": 2090, + "Objects": [{ + "StartTime": 2090, + "Position": 238 + }] + }, + { + "StartTime": 2172, + "Objects": [{ + "StartTime": 2172, + "Position": 243 + }] + }, + { + "StartTime": 2254, + "Objects": [{ + "StartTime": 2254, + "Position": 278 + }] + }, + { + "StartTime": 2336, + "Objects": [{ + "StartTime": 2336, + "Position": 278 + }] + }, + { + "StartTime": 2418, + "Objects": [{ + "StartTime": 2418, + "Position": 238 + }] + }, + { + "StartTime": 2500, + "Objects": [{ + "StartTime": 2500, + "Position": 258 + }] + }, + { + "StartTime": 2582, + "Objects": [{ + "StartTime": 2582, + "Position": 256 + }] + }, + { + "StartTime": 2664, + "Objects": [{ + "StartTime": 2664, + "Position": 242 + }] + }, + { + "StartTime": 2746, + "Objects": [{ + "StartTime": 2746, + "Position": 238 + }] + }, + { + "StartTime": 2828, + "Objects": [{ + "StartTime": 2828, + "Position": 238 + }] + }, + { + "StartTime": 2909, + "Objects": [{ + "StartTime": 2909, + "Position": 271 + }] + }, + { + "StartTime": 2991, + "Objects": [{ + "StartTime": 2991, + "Position": 254 + }] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu new file mode 100644 index 0000000000..321af4604b --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu @@ -0,0 +1,50 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +369,327.868852459016,4,2,2,32,1,0 + +[HitObjects] +258,189,369,1,0,0:0:0:0: +258,189,450,1,0,0:0:0:0: +258,189,532,1,0,0:0:0:0: +258,189,614,1,0,0:0:0:0: +258,189,696,1,0,0:0:0:0: +258,189,778,1,0,0:0:0:0: +258,189,860,1,0,0:0:0:0: +258,189,942,1,0,0:0:0:0: +258,189,1024,1,0,0:0:0:0: +258,189,1106,1,0,0:0:0:0: +258,189,1188,1,0,0:0:0:0: +258,189,1270,1,0,0:0:0:0: +258,189,1352,1,0,0:0:0:0: +258,189,1434,1,0,0:0:0:0: +258,189,1516,1,0,0:0:0:0: +258,189,1598,1,0,0:0:0:0: +258,189,1680,1,0,0:0:0:0: +258,189,1762,1,0,0:0:0:0: +258,189,1844,1,0,0:0:0:0: +258,189,1926,1,0,0:0:0:0: +258,189,2008,1,0,0:0:0:0: +258,189,2090,1,0,0:0:0:0: +258,189,2172,1,0,0:0:0:0: +258,189,2254,1,0,0:0:0:0: +258,189,2336,1,0,0:0:0:0: +258,189,2418,1,0,0:0:0:0: +258,189,2500,1,0,0:0:0:0: +258,189,2582,1,0,0:0:0:0: +258,189,2664,1,0,0:0:0:0: +258,189,2746,1,0,0:0:0:0: +258,189,2828,1,0,0:0:0:0: +258,189,2909,1,0,0:0:0:0: +258,189,2991,1,0,0:0:0:0: \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 6b95975059..6f10540973 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; [TestCase("basic")] - public new void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { @@ -35,11 +32,37 @@ namespace osu.Game.Rulesets.Mania.Tests }; } - protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter); + private readonly Dictionary rngSnapshots = new Dictionary(); + + protected override void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) + { + base.OnConversionGenerated(original, result, beatmapConverter); + + rngSnapshots[original] = new RngSnapshot(beatmapConverter); + } + + protected override ManiaConvertMapping CreateConvertMapping(HitObject source) => new ManiaConvertMapping(rngSnapshots[source]); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } + public class RngSnapshot + { + public readonly uint RandomW; + public readonly uint RandomX; + public readonly uint RandomY; + public readonly uint RandomZ; + + public RngSnapshot(IBeatmapConverter converter) + { + var maniaConverter = (ManiaBeatmapConverter)converter; + RandomW = maniaConverter.Random.W; + RandomX = maniaConverter.Random.X; + RandomY = maniaConverter.Random.Y; + RandomZ = maniaConverter.Random.Z; + } + } + public class ManiaConvertMapping : ConvertMapping, IEquatable { public uint RandomW; @@ -51,13 +74,12 @@ namespace osu.Game.Rulesets.Mania.Tests { } - public ManiaConvertMapping(IBeatmapConverter converter) + public ManiaConvertMapping(RngSnapshot snapshot) { - var maniaConverter = (ManiaBeatmapConverter)converter; - RandomW = maniaConverter.Random.W; - RandomX = maniaConverter.Random.X; - RandomY = maniaConverter.Random.Y; - RandomZ = maniaConverter.Random.Z; + RandomW = snapshot.RandomW; + RandomX = snapshot.RandomX; + RandomY = snapshot.RandomY; + RandomZ = snapshot.RandomZ; } public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 451ecf97ea..392170f0a8 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -23,10 +23,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase("slider-ticks")] [TestCase("repeat-slider")] [TestCase("uneven-repeat-slider")] - public new void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 68ae7544c2..6c1882b4e2 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [NonParallelizable] [TestCase("basic")] [TestCase("slider-generating-drumroll")] - public new void Test(string name) - { - base.Test(name); - } + public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) { diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs index c344cb9598..1f5ba67e03 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -9,6 +9,8 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; +using osu.Game.Users; +using osu.Framework.Bindables; namespace osu.Game.Tests.Visual.Online { @@ -23,18 +25,25 @@ namespace osu.Game.Tests.Visual.Online public TestSceneProfileRulesetSelector() { ProfileRulesetSelector selector; + Bindable user = new Bindable(); Child = selector = new ProfileRulesetSelector { Anchor = Anchor.Centre, Origin = Anchor.Centre, + User = { BindTarget = user } }; AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo)); AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); - AddStep("select default ruleset", selector.SelectDefaultRuleset); + + AddStep("User with osu as default", () => user.Value = new User { PlayMode = "osu" }); + AddStep("User with mania as default", () => user.Value = new User { PlayMode = "mania" }); + AddStep("User with taiko as default", () => user.Value = new User { PlayMode = "taiko" }); + AddStep("User with catch as default", () => user.Value = new User { PlayMode = "fruits" }); + AddStep("null user", () => user.Value = null); } } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c6105f6566..8605caa5fe 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -89,6 +89,14 @@ namespace osu.Game.Beatmaps return path; } + /// + /// Creates a to convert a for a specified . + /// + /// The to be converted. + /// The for which should be converted. + /// The applicable . + protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); + /// /// Constructs a playable from using the applicable converters for a specific . /// @@ -104,7 +112,7 @@ namespace osu.Game.Beatmaps { var rulesetInstance = ruleset.CreateInstance(); - IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap); + IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); // Check if the beatmap can be converted if (!converter.CanConvert) diff --git a/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs new file mode 100644 index 0000000000..b7d2222f33 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Graphics.UserInterface +{ + public class DimmedLoadingLayer : VisibilityContainer + { + private const float transition_duration = 250; + + private readonly LoadingAnimation loading; + + public DimmedLoadingLayer() + { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + loading = new LoadingAnimation(), + }; + } + + protected override void PopIn() + { + this.FadeIn(transition_duration, Easing.OutQuint); + loading.Show(); + } + + protected override void PopOut() + { + this.FadeOut(transition_duration, Easing.OutQuint); + loading.Hide(); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index d158186899..544acc7eb2 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -81,7 +81,8 @@ namespace osu.Game.Graphics.UserInterface Colour = Color4.White, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - } + }, + new HoverClickSounds() }; Current.ValueChanged += selected => diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index d9d05ff285..c8c36789c4 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -27,24 +27,25 @@ namespace osu.Game.Online.API.Requests } // ReSharper disable once ImpureMethodCallOnReadonlyValueField - protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}"; + protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={searchCategory.ToString().ToLowerInvariant()}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}"; } public enum BeatmapSearchCategory { - Any = 7, + Any, - [Description("Ranked & Approved")] - RankedApproved = 0, - Qualified = 3, - Loved = 8, - Favourites = 2, + [Description("Has Leaderboard")] + Leaderboard, + Ranked, + Qualified, + Loved, + Favourites, [Description("Pending & WIP")] - PendingWIP = 4, - Graveyard = 5, + Pending, + Graveyard, [Description("My Maps")] - MyMaps = 6, + Mine, } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 62d6efcb6f..7bfdc7ff69 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -48,22 +48,24 @@ namespace osu.Game.Online attachDownload(manager.GetExistingDownload(modelInfo.NewValue)); }, true); - manager.DownloadBegan += download => - { - if (download.Model.Equals(Model.Value)) - attachDownload(download); - }; - - manager.DownloadFailed += download => - { - if (download.Model.Equals(Model.Value)) - attachDownload(null); - }; - + manager.DownloadBegan += downloadBegan; + manager.DownloadFailed += downloadFailed; manager.ItemAdded += itemAdded; manager.ItemRemoved += itemRemoved; } + private void downloadBegan(ArchiveDownloadRequest request) + { + if (request.Model.Equals(Model.Value)) + attachDownload(request); + } + + private void downloadFailed(ArchiveDownloadRequest request) + { + if (request.Model.Equals(Model.Value)) + attachDownload(null); + } + private ArchiveDownloadRequest attachedRequest; private void attachDownload(ArchiveDownloadRequest request) @@ -126,8 +128,10 @@ namespace osu.Game.Online if (manager != null) { - manager.DownloadBegan -= attachDownload; + manager.DownloadBegan -= downloadBegan; + manager.DownloadFailed -= downloadFailed; manager.ItemAdded -= itemAdded; + manager.ItemRemoved -= itemRemoved; } State.UnbindAll(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index c478ad0a57..b64f04de2a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -328,7 +328,9 @@ namespace osu.Game if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; - beatmap.OldValue?.Dispose(); + using (var oldBeatmap = beatmap.OldValue) + if (oldBeatmap?.Track != null) + oldBeatmap.Track.Completed -= currentTrackCompleted; nextBeatmap?.LoadBeatmapAsync(); } diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 4b43542b43..8b04bf0387 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Direct protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; + protected override BeatmapSearchCategory DefaultCategory => BeatmapSearchCategory.Leaderboard; protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector(); diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs index b6112a6501..2c9a3dd5f9 100644 --- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs @@ -2,11 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Rulesets; +using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -16,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Header.Components { private Color4 accentColour = Color4.White; + public readonly Bindable User = new Bindable(); + public ProfileRulesetSelector() { TabContainer.Masking = false; @@ -32,24 +36,17 @@ namespace osu.Game.Overlays.Profile.Header.Components ((ProfileRulesetTabItem)tabItem).AccentColour = accentColour; } - public void SetDefaultRuleset(RulesetInfo ruleset) + protected override void LoadComplete() { - // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value - foreach (TabItem tabItem in TabContainer) - ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID; + base.LoadComplete(); + + User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true); } - public void SelectDefaultRuleset() + public void SetDefaultRuleset(RulesetInfo ruleset) { - // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value foreach (TabItem tabItem in TabContainer) - { - if (((ProfileRulesetTabItem)tabItem).IsDefault) - { - Current.Value = ((ProfileRulesetTabItem)tabItem).Value; - return; - } - } + ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID; } protected override TabItem CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index c5e61f68f4..fa60a37ddb 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -80,7 +80,6 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load(OsuColour colours) { background.Colour = colours.Pink; - iconContainer.Colour = colours.GreySeafoam; } } } diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs index b0a8a0e77d..a0c4e9a080 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -26,6 +26,7 @@ namespace osu.Game.Overlays.SearchableList protected abstract Color4 BackgroundColour { get; } protected abstract T DefaultTab { get; } + protected abstract U DefaultCategory { get; } protected virtual Drawable CreateSupplementaryControls() => null; /// @@ -109,6 +110,9 @@ namespace osu.Game.Overlays.SearchableList Tabs.Current.Value = DefaultTab; Tabs.Current.TriggerChange(); + + DisplayStyleControl.Dropdown.Current.Value = DefaultCategory; + DisplayStyleControl.Dropdown.Current.TriggerChange(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index a80b7c1108..358f94b659 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -84,6 +84,7 @@ namespace osu.Game.Overlays.Settings Content.Anchor = Anchor.CentreLeft; Content.Origin = Anchor.CentreLeft; RelativeSizeAxes = Axes.Both; + ScrollbarVisible = false; } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 474f529bb1..9dd0def453 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -186,7 +186,7 @@ namespace osu.Game.Overlays base.UpdateAfterChildren(); ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 }; - ContentContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; + Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 }; } protected class SettingsSectionsContainer : SectionsContainer diff --git a/osu.Game/Overlays/Social/FilterControl.cs b/osu.Game/Overlays/Social/FilterControl.cs index 6abf6ec98d..1c2cb95dfe 100644 --- a/osu.Game/Overlays/Social/FilterControl.cs +++ b/osu.Game/Overlays/Social/FilterControl.cs @@ -12,6 +12,7 @@ namespace osu.Game.Overlays.Social { protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a"); protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank; + protected override SortDirection DefaultCategory => SortDirection.Ascending; public FilterControl() { diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index 8e14f76e4b..d0d983bbff 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -14,6 +14,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { protected override Color4 BackgroundColour => OsuColour.FromHex(@"362e42"); protected override PrimaryFilter DefaultTab => PrimaryFilter.Open; + protected override SecondaryFilter DefaultCategory => SecondaryFilter.Public; protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING; diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 23dd87d8ea..577d999388 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Select private readonly MetadataSection description, source, tags; private readonly Container failRetryContainer; private readonly FailRetryGraph failRetryGraph; - private readonly DimmedLoadingAnimation loading; + private readonly DimmedLoadingLayer loading; private IAPIProvider api; @@ -156,10 +156,7 @@ namespace osu.Game.Screens.Select }, }, }, - loading = new DimmedLoadingAnimation - { - RelativeSizeAxes = Axes.Both, - }, + loading = new DimmedLoadingLayer(), }; } @@ -365,35 +362,5 @@ namespace osu.Game.Screens.Select }); } } - - private class DimmedLoadingAnimation : VisibilityContainer - { - private readonly LoadingAnimation loading; - - public DimmedLoadingAnimation() - { - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - loading = new LoadingAnimation(), - }; - } - - protected override void PopIn() - { - this.FadeIn(transition_duration, Easing.OutQuint); - loading.Show(); - } - - protected override void PopOut() - { - this.FadeOut(transition_duration, Easing.OutQuint); - loading.Hide(); - } - } } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 6a5e17eb38..a555a52e42 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -4,13 +4,16 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using Newtonsoft.Json; using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; namespace osu.Game.Tests.Beatmaps @@ -25,11 +28,9 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } - protected IBeatmapConverter Converter { get; private set; } - - protected void Test(string name) + protected void Test(string name, params Type[] mods) { - var ourResult = convert(name); + var ourResult = convert(name, mods.Select(m => (Mod)Activator.CreateInstance(m)).ToArray()); var expectedResult = read(name); Assert.Multiple(() => @@ -91,33 +92,40 @@ namespace osu.Game.Tests.Beatmaps }); } - private ConvertResult convert(string name) + private ConvertResult convert(string name, Mod[] mods) { var beatmap = getBeatmap(name); var rulesetInstance = CreateRuleset(); beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo(); - Converter = rulesetInstance.CreateBeatmapConverter(beatmap); + var converterResult = new Dictionary>(); - var result = new ConvertResult(); - - Converter.ObjectConverted += (orig, converted) => + var working = new ConversionWorkingBeatmap(beatmap) { - converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty)); - - var mapping = CreateConvertMapping(); - mapping.StartTime = orig.StartTime; - - foreach (var obj in converted) - mapping.Objects.AddRange(CreateConvertValue(obj)); - result.Mappings.Add(mapping); + ConversionGenerated = (o, r, c) => + { + converterResult[o] = r; + OnConversionGenerated(o, r, c); + } }; - IBeatmap convertedBeatmap = Converter.Convert(); - rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess(); + working.GetPlayableBeatmap(rulesetInstance.RulesetInfo, mods); - return result; + return new ConvertResult + { + Mappings = converterResult.Select(r => + { + var mapping = CreateConvertMapping(r.Key); + mapping.StartTime = r.Key.StartTime; + mapping.Objects.AddRange(r.Value.SelectMany(CreateConvertValue)); + return mapping; + }).ToList() + }; + } + + protected virtual void OnConversionGenerated(HitObject original, IEnumerable result, IBeatmapConverter beatmapConverter) + { } private ConvertResult read(string name) @@ -154,7 +162,7 @@ namespace osu.Game.Tests.Beatmaps /// This should be used to validate the integrity of the conversion process after a conversion has occurred. /// /// - protected virtual TConvertMapping CreateConvertMapping() => new TConvertMapping(); + protected virtual TConvertMapping CreateConvertMapping(HitObject source) => new TConvertMapping(); /// /// Creates the conversion value for a . A conversion value stores information about the converted . @@ -176,6 +184,32 @@ namespace osu.Game.Tests.Beatmaps [JsonProperty] public List Mappings = new List(); } + + private class ConversionWorkingBeatmap : WorkingBeatmap + { + public Action, IBeatmapConverter> ConversionGenerated; + + private readonly IBeatmap beatmap; + + public ConversionWorkingBeatmap(IBeatmap beatmap) + : base(beatmap.BeatmapInfo, null) + { + this.beatmap = beatmap; + } + + protected override IBeatmap GetBeatmap() => beatmap; + + protected override Texture GetBackground() => throw new NotImplementedException(); + + protected override Track GetTrack() => throw new NotImplementedException(); + + protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) + { + var converter = base.CreateBeatmapConverter(beatmap, ruleset); + converter.ObjectConverted += (orig, converted) => ConversionGenerated?.Invoke(orig, converted, converter); + return converter; + } + } } public abstract class BeatmapConversionTest : BeatmapConversionTest, TConvertValue> diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1f91ce1cd8..cf325b77be 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index 5fe8cd6d5b..f3067bdcec 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -14,8 +14,6 @@ 0.1.0 LSRequiresIPhoneOS - LSSupportsOpeningDocumentsInPlace - MinimumOSVersion 10.0 UIDeviceFamily