diff --git a/appveyor.yml b/appveyor.yml index b86082334d..e63f6ea55c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,6 +20,10 @@ build: project: osu.sln parallel: true verbosity: minimal +test: + assemblies: + only: + - 'osu.Desktop\**\*.dll' after_build: - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file diff --git a/osu-framework b/osu-framework index 9a773e62eb..e8ae207769 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9a773e62eb246206b918ba4fccf9f2507aaa4595 +Subproject commit e8ae207769ec26fb7ddd67a2433514fcda354ecd diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 048fe93c11..d036a6822c 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Runtime; using osu.Framework; using osu.Framework.Platform; using osu.Game.IPC; @@ -15,6 +16,9 @@ namespace osu.Desktop [STAThread] public static int Main(string[] args) { + if (!RuntimeInfo.IsMono) + useMulticoreJit(); + // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; @@ -44,8 +48,16 @@ namespace osu.Desktop break; } } + return 0; } } + + private static void useMulticoreJit() + { + var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles")); + ProfileOptimization.SetProfileRoot(directory.FullName); + ProfileOptimization.StartProfile("Startup.Profile"); + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index be1e360fce..a3e5aba2db 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = lastTickTime, ComboColour = ComboColour, - X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = spanStartTime + t, ComboColour = ComboColour, - X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Catch.Objects Samples = Samples, ComboColour = ComboColour, StartTime = spanStartTime + spanDuration, - X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH + X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH }); } } public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime; diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-expected-conversion.json new file mode 100644 index 0000000000..9357d3b75c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -0,0 +1,957 @@ +{ + "Mappings": [{ + "StartTime": 500.0, + "Objects": [{ + "StartTime": 500.0, + "Position": 96.0 + }, { + "StartTime": 562.0, + "Position": 100.84 + }, { + "StartTime": 625.0, + "Position": 125.0 + }, { + "StartTime": 687.0, + "Position": 152.84 + }, { + "StartTime": 750.0, + "Position": 191.0 + }, { + "StartTime": 812.0, + "Position": 212.84 + }, { + "StartTime": 875.0, + "Position": 217.0 + }, { + "StartTime": 937.0, + "Position": 234.84 + }, { + "StartTime": 1000.0, + "Position": 256.0 + }, { + "StartTime": 1062.0, + "Position": 267.84 + }, { + "StartTime": 1125.0, + "Position": 284.0 + }, { + "StartTime": 1187.0, + "Position": 311.84 + }, { + "StartTime": 1250.0, + "Position": 350.0 + }, { + "StartTime": 1312.0, + "Position": 359.84 + }, { + "StartTime": 1375.0, + "Position": 367.0 + }, { + "StartTime": 1437.0, + "Position": 400.84 + }, { + "StartTime": 1500.0, + "Position": 416.0 + }, { + "StartTime": 1562.0, + "Position": 377.159973 + }, { + "StartTime": 1625.0, + "Position": 367.0 + }, { + "StartTime": 1687.0, + "Position": 374.159973 + }, { + "StartTime": 1750.0, + "Position": 353.0 + }, { + "StartTime": 1812.0, + "Position": 329.159973 + }, { + "StartTime": 1875.0, + "Position": 288.0 + }, { + "StartTime": 1937.0, + "Position": 259.159973 + }, { + "StartTime": 2000.0, + "Position": 256.0 + }, { + "StartTime": 2058.0, + "Position": 232.44 + }, { + "StartTime": 2116.0, + "Position": 222.879974 + }, { + "StartTime": 2174.0, + "Position": 185.319992 + }, { + "StartTime": 2232.0, + "Position": 177.76001 + }, { + "StartTime": 2290.0, + "Position": 162.200012 + }, { + "StartTime": 2348.0, + "Position": 158.639984 + }, { + "StartTime": 2406.0, + "Position": 111.079994 + }, { + "StartTime": 2500.0, + "Position": 96.0 + }] + }, { + "StartTime": 3000.0, + "Objects": [{ + "StartTime": 3000.0, + "Position": 18.0 + }, { + "StartTime": 3062.0, + "Position": 482.0 + }, { + "StartTime": 3125.0, + "Position": 243.0 + }, { + "StartTime": 3187.0, + "Position": 332.0 + }, { + "StartTime": 3250.0, + "Position": 477.0 + }, { + "StartTime": 3312.0, + "Position": 376.0 + }, { + "StartTime": 3375.0, + "Position": 104.0 + }, { + "StartTime": 3437.0, + "Position": 156.0 + }, { + "StartTime": 3500.0, + "Position": 135.0 + }, { + "StartTime": 3562.0, + "Position": 256.0 + }, { + "StartTime": 3625.0, + "Position": 360.0 + }, { + "StartTime": 3687.0, + "Position": 199.0 + }, { + "StartTime": 3750.0, + "Position": 239.0 + }, { + "StartTime": 3812.0, + "Position": 326.0 + }, { + "StartTime": 3875.0, + "Position": 393.0 + }, { + "StartTime": 3937.0, + "Position": 470.0 + }, { + "StartTime": 4000.0, + "Position": 136.0 + }] + }, { + "StartTime": 4500.0, + "Objects": [{ + "StartTime": 4500.0, + "Position": 317.0 + }, { + "StartTime": 4562.0, + "Position": 354.0 + }, { + "StartTime": 4625.0, + "Position": 414.0 + }, { + "StartTime": 4687.0, + "Position": 39.0 + }, { + "StartTime": 4750.0, + "Position": 172.0 + }, { + "StartTime": 4812.0, + "Position": 479.0 + }, { + "StartTime": 4875.0, + "Position": 18.0 + }, { + "StartTime": 4937.0, + "Position": 151.0 + }, { + "StartTime": 5000.0, + "Position": 342.0 + }, { + "StartTime": 5062.0, + "Position": 400.0 + }, { + "StartTime": 5125.0, + "Position": 420.0 + }, { + "StartTime": 5187.0, + "Position": 90.0 + }, { + "StartTime": 5250.0, + "Position": 220.0 + }, { + "StartTime": 5312.0, + "Position": 80.0 + }, { + "StartTime": 5375.0, + "Position": 421.0 + }, { + "StartTime": 5437.0, + "Position": 473.0 + }, { + "StartTime": 5500.0, + "Position": 97.0 + }] + }, { + "StartTime": 6000.0, + "Objects": [{ + "StartTime": 6000.0, + "Position": 105.0 + }, { + "StartTime": 6062.0, + "Position": 249.0 + }, { + "StartTime": 6125.0, + "Position": 163.0 + }, { + "StartTime": 6187.0, + "Position": 194.0 + }, { + "StartTime": 6250.0, + "Position": 106.0 + }, { + "StartTime": 6312.0, + "Position": 212.0 + }, { + "StartTime": 6375.0, + "Position": 257.0 + }, { + "StartTime": 6437.0, + "Position": 461.0 + }, { + "StartTime": 6500.0, + "Position": 79.0 + }] + }, { + "StartTime": 7000.0, + "Objects": [{ + "StartTime": 7000.0, + "Position": 256.0 + }, { + "StartTime": 7062.0, + "Position": 294.84 + }, { + "StartTime": 7125.0, + "Position": 279.0 + }, { + "StartTime": 7187.0, + "Position": 309.84 + }, { + "StartTime": 7250.0, + "Position": 336.0 + }, { + "StartTime": 7312.0, + "Position": 322.16 + }, { + "StartTime": 7375.0, + "Position": 308.0 + }, { + "StartTime": 7437.0, + "Position": 263.16 + }, { + "StartTime": 7500.0, + "Position": 256.0 + }, { + "StartTime": 7562.0, + "Position": 261.84 + }, { + "StartTime": 7625.0, + "Position": 277.0 + }, { + "StartTime": 7687.0, + "Position": 318.84 + }, { + "StartTime": 7750.0, + "Position": 336.0 + }, { + "StartTime": 7803.0, + "Position": 305.04 + }, { + "StartTime": 7857.0, + "Position": 307.76 + }, { + "StartTime": 7910.0, + "Position": 297.8 + }, { + "StartTime": 8000.0, + "Position": 256.0 + }] + }, { + "StartTime": 8500.0, + "Objects": [{ + "StartTime": 8500.0, + "Position": 32.0 + }, { + "StartTime": 8562.0, + "Position": 22.8515015 + }, { + "StartTime": 8625.0, + "Position": 28.5659637 + }, { + "StartTime": 8687.0, + "Position": 50.3433228 + }, { + "StartTime": 8750.0, + "Position": 56.58974 + }, { + "StartTime": 8812.0, + "Position": 64.23422 + }, { + "StartTime": 8875.0, + "Position": 67.7117844 + }, { + "StartTime": 8937.0, + "Position": 90.52607 + }, { + "StartTime": 9000.0, + "Position": 101.81015 + }, { + "StartTime": 9062.0, + "Position": 113.478188 + }, { + "StartTime": 9125.0, + "Position": 159.414444 + }, { + "StartTime": 9187.0, + "Position": 155.1861 + }, { + "StartTime": 9250.0, + "Position": 179.600418 + }, { + "StartTime": 9312.0, + "Position": 212.293015 + }, { + "StartTime": 9375.0, + "Position": 197.2076 + }, { + "StartTime": 9437.0, + "Position": 243.438324 + }, { + "StartTime": 9500.0, + "Position": 237.2304 + }, { + "StartTime": 9562.0, + "Position": 241.253983 + }, { + "StartTime": 9625.0, + "Position": 258.950623 + }, { + "StartTime": 9687.0, + "Position": 253.3786 + }, { + "StartTime": 9750.0, + "Position": 270.8865 + }, { + "StartTime": 9812.0, + "Position": 244.38974 + }, { + "StartTime": 9875.0, + "Position": 242.701874 + }, { + "StartTime": 9937.0, + "Position": 256.2331 + }, { + "StartTime": 10000.0, + "Position": 270.339874 + }, { + "StartTime": 10062.0, + "Position": 275.9349 + }, { + "StartTime": 10125.0, + "Position": 297.2969 + }, { + "StartTime": 10187.0, + "Position": 307.834137 + }, { + "StartTime": 10250.0, + "Position": 321.6449 + }, { + "StartTime": 10312.0, + "Position": 357.746338 + }, { + "StartTime": 10375.0, + "Position": 358.21875 + }, { + "StartTime": 10437.0, + "Position": 394.943 + }, { + "StartTime": 10500.0, + "Position": 401.0588 + }, { + "StartTime": 10558.0, + "Position": 418.21347 + }, { + "StartTime": 10616.0, + "Position": 424.6034 + }, { + "StartTime": 10674.0, + "Position": 455.835754 + }, { + "StartTime": 10732.0, + "Position": 477.5042 + }, { + "StartTime": 10790.0, + "Position": 476.290955 + }, { + "StartTime": 10848.0, + "Position": 470.943237 + }, { + "StartTime": 10906.0, + "Position": 503.3372 + }, { + "StartTime": 10999.0, + "Position": 508.166229 + }] + }, { + "StartTime": 11500.0, + "Objects": [{ + "StartTime": 11500.0, + "Position": 321.0 + }, { + "StartTime": 11562.0, + "Position": 17.0 + }, { + "StartTime": 11625.0, + "Position": 173.0 + }, { + "StartTime": 11687.0, + "Position": 170.0 + }, { + "StartTime": 11750.0, + "Position": 447.0 + }, { + "StartTime": 11812.0, + "Position": 218.0 + }, { + "StartTime": 11875.0, + "Position": 394.0 + }, { + "StartTime": 11937.0, + "Position": 46.0 + }, { + "StartTime": 12000.0, + "Position": 480.0 + }] + }, { + "StartTime": 12500.0, + "Objects": [{ + "StartTime": 12500.0, + "Position": 512.0 + }, { + "StartTime": 12562.0, + "Position": 491.3132 + }, { + "StartTime": 12625.0, + "Position": 484.3089 + }, { + "StartTime": 12687.0, + "Position": 454.6221 + }, { + "StartTime": 12750.0, + "Position": 433.617767 + }, { + "StartTime": 12812.0, + "Position": 399.930969 + }, { + "StartTime": 12875.0, + "Position": 395.926666 + }, { + "StartTime": 12937.0, + "Position": 361.239868 + }, { + "StartTime": 13000.0, + "Position": 353.235535 + }, { + "StartTime": 13062.0, + "Position": 314.548767 + }, { + "StartTime": 13125.0, + "Position": 315.544434 + }, { + "StartTime": 13187.0, + "Position": 288.857635 + }, { + "StartTime": 13250.0, + "Position": 254.853333 + }, { + "StartTime": 13312.0, + "Position": 239.166534 + }, { + "StartTime": 13375.0, + "Position": 240.1622 + }, { + "StartTime": 13437.0, + "Position": 212.4754 + }, { + "StartTime": 13500.0, + "Position": 194.471069 + }, { + "StartTime": 13562.0, + "Position": 161.784271 + }, { + "StartTime": 13625.0, + "Position": 145.779968 + }, { + "StartTime": 13687.0, + "Position": 129.09314 + }, { + "StartTime": 13750.0, + "Position": 104.088837 + }, { + "StartTime": 13812.0, + "Position": 95.40204 + }, { + "StartTime": 13875.0, + "Position": 61.3977356 + }, { + "StartTime": 13937.0, + "Position": 56.710907 + }, { + "StartTime": 14000.0, + "Position": 35.7066345 + }, { + "StartTime": 14062.0, + "Position": 5.019806 + }, { + "StartTime": 14125.0, + "Position": 0.0 + }, { + "StartTime": 14187.0, + "Position": 39.7696266 + }, { + "StartTime": 14250.0, + "Position": 23.0119171 + }, { + "StartTime": 14312.0, + "Position": 75.94882 + }, { + "StartTime": 14375.0, + "Position": 98.19112 + }, { + "StartTime": 14437.0, + "Position": 82.12803 + }, { + "StartTime": 14500.0, + "Position": 118.370323 + }, { + "StartTime": 14562.0, + "Position": 149.307236 + }, { + "StartTime": 14625.0, + "Position": 168.549515 + }, { + "StartTime": 14687.0, + "Position": 190.486435 + }, { + "StartTime": 14750.0, + "Position": 186.728714 + }, { + "StartTime": 14812.0, + "Position": 199.665634 + }, { + "StartTime": 14875.0, + "Position": 228.907928 + }, { + "StartTime": 14937.0, + "Position": 264.844849 + }, { + "StartTime": 15000.0, + "Position": 271.087128 + }, { + "StartTime": 15062.0, + "Position": 290.024017 + }, { + "StartTime": 15125.0, + "Position": 302.266327 + }, { + "StartTime": 15187.0, + "Position": 344.203247 + }, { + "StartTime": 15250.0, + "Position": 356.445526 + }, { + "StartTime": 15312.0, + "Position": 359.382446 + }, { + "StartTime": 15375.0, + "Position": 401.624725 + }, { + "StartTime": 15437.0, + "Position": 388.561646 + }, { + "StartTime": 15500.0, + "Position": 423.803925 + }, { + "StartTime": 15562.0, + "Position": 425.740845 + }, { + "StartTime": 15625.0, + "Position": 449.983124 + }, { + "StartTime": 15687.0, + "Position": 468.920044 + }, { + "StartTime": 15750.0, + "Position": 492.162323 + }, { + "StartTime": 15812.0, + "Position": 506.784332 + }, { + "StartTime": 15875.0, + "Position": 474.226227 + }, { + "StartTime": 15937.0, + "Position": 482.978638 + }, { + "StartTime": 16000.0, + "Position": 446.420532 + }, { + "StartTime": 16058.0, + "Position": 418.4146 + }, { + "StartTime": 16116.0, + "Position": 425.408844 + }, { + "StartTime": 16174.0, + "Position": 383.402924 + }, { + "StartTime": 16232.0, + "Position": 363.397156 + }, { + "StartTime": 16290.0, + "Position": 343.391235 + }, { + "StartTime": 16348.0, + "Position": 328.385468 + }, { + "StartTime": 16406.0, + "Position": 322.3797 + }, { + "StartTime": 16500.0, + "Position": 291.1977 + }] + }, { + "StartTime": 17000.0, + "Objects": [{ + "StartTime": 17000.0, + "Position": 256.0 + }, { + "StartTime": 17062.0, + "Position": 228.16 + }, { + "StartTime": 17125.0, + "Position": 234.0 + }, { + "StartTime": 17187.0, + "Position": 202.16 + }, { + "StartTime": 17250.0, + "Position": 176.0 + }, { + "StartTime": 17312.0, + "Position": 210.84 + }, { + "StartTime": 17375.0, + "Position": 221.0 + }, { + "StartTime": 17437.0, + "Position": 219.84 + }, { + "StartTime": 17500.0, + "Position": 256.0 + }, { + "StartTime": 17562.0, + "Position": 219.16 + }, { + "StartTime": 17625.0, + "Position": 228.0 + }, { + "StartTime": 17687.0, + "Position": 203.16 + }, { + "StartTime": 17750.0, + "Position": 176.0 + }, { + "StartTime": 17803.0, + "Position": 174.959991 + }, { + "StartTime": 17857.0, + "Position": 214.23999 + }, { + "StartTime": 17910.0, + "Position": 228.200012 + }, { + "StartTime": 18000.0, + "Position": 256.0 + }] + }, { + "StartTime": 18500.0, + "Objects": [{ + "StartTime": 18500.0, + "Position": 362.0 + }, { + "StartTime": 18559.0, + "Position": 249.0 + }, { + "StartTime": 18618.0, + "Position": 357.0 + }, { + "StartTime": 18678.0, + "Position": 167.0 + }, { + "StartTime": 18737.0, + "Position": 477.0 + }, { + "StartTime": 18796.0, + "Position": 411.0 + }, { + "StartTime": 18856.0, + "Position": 254.0 + }, { + "StartTime": 18915.0, + "Position": 308.0 + }, { + "StartTime": 18975.0, + "Position": 399.0 + }, { + "StartTime": 19034.0, + "Position": 176.0 + }, { + "StartTime": 19093.0, + "Position": 14.0 + }, { + "StartTime": 19153.0, + "Position": 258.0 + }, { + "StartTime": 19212.0, + "Position": 221.0 + }, { + "StartTime": 19271.0, + "Position": 481.0 + }, { + "StartTime": 19331.0, + "Position": 92.0 + }, { + "StartTime": 19390.0, + "Position": 211.0 + }, { + "StartTime": 19450.0, + "Position": 135.0 + }] + }, { + "StartTime": 19875.0, + "Objects": [{ + "StartTime": 19875.0, + "Position": 216.0 + }, { + "StartTime": 19937.0, + "Position": 215.307053 + }, { + "StartTime": 20000.0, + "Position": 236.036865 + }, { + "StartTime": 20062.0, + "Position": 236.312088 + }, { + "StartTime": 20125.0, + "Position": 235.838928 + }, { + "StartTime": 20187.0, + "Position": 269.9743 + }, { + "StartTime": 20250.0, + "Position": 285.999146 + }, { + "StartTime": 20312.0, + "Position": 283.669067 + }, { + "StartTime": 20375.0, + "Position": 317.446747 + }, { + "StartTime": 20437.0, + "Position": 330.750275 + }, { + "StartTime": 20500.0, + "Position": 344.0156 + }, { + "StartTime": 20562.0, + "Position": 318.472168 + }, { + "StartTime": 20625.0, + "Position": 309.165466 + }, { + "StartTime": 20687.0, + "Position": 317.044617 + }, { + "StartTime": 20750.0, + "Position": 280.457367 + }, { + "StartTime": 20812.0, + "Position": 272.220581 + }, { + "StartTime": 20875.0, + "Position": 270.3294 + }, { + "StartTime": 20937.0, + "Position": 262.57605 + }, { + "StartTime": 21000.0, + "Position": 244.803329 + }, { + "StartTime": 21062.0, + "Position": 215.958359 + }, { + "StartTime": 21125.0, + "Position": 177.79332 + }, { + "StartTime": 21187.0, + "Position": 190.948349 + }, { + "StartTime": 21250.0, + "Position": 158.78334 + }, { + "StartTime": 21312.0, + "Position": 136.93837 + }, { + "StartTime": 21375.0, + "Position": 119.121056 + }, { + "StartTime": 21437.0, + "Position": 132.387573 + }, { + "StartTime": 21500.0, + "Position": 124.503014 + }, { + "StartTime": 21562.0, + "Position": 118.749374 + }, { + "StartTime": 21625.0, + "Position": 123.165535 + }, { + "StartTime": 21687.0, + "Position": 96.02999 + }, { + "StartTime": 21750.0, + "Position": 118.547928 + }, { + "StartTime": 21812.0, + "Position": 128.856232 + }, { + "StartTime": 21875.0, + "Position": 124.28746 + }, { + "StartTime": 21937.0, + "Position": 150.754929 + }, { + "StartTime": 22000.0, + "Position": 149.528732 + }, { + "StartTime": 22062.0, + "Position": 145.1691 + }, { + "StartTime": 22125.0, + "Position": 182.802155 + }, { + "StartTime": 22187.0, + "Position": 178.6452 + }, { + "StartTime": 22250.0, + "Position": 213.892181 + }, { + "StartTime": 22312.0, + "Position": 218.713028 + }, { + "StartTime": 22375.0, + "Position": 240.4715 + }, { + "StartTime": 22437.0, + "Position": 239.371887 + }, { + "StartTime": 22500.0, + "Position": 261.907257 + }, { + "StartTime": 22562.0, + "Position": 314.353119 + }, { + "StartTime": 22625.0, + "Position": 299.273376 + }, { + "StartTime": 22687.0, + "Position": 356.98288 + }, { + "StartTime": 22750.0, + "Position": 339.078552 + }, { + "StartTime": 22812.0, + "Position": 377.8958 + }, { + "StartTime": 22875.0, + "Position": 398.054047 + }, { + "StartTime": 22937.0, + "Position": 398.739441 + }, { + "StartTime": 23000.0, + "Position": 407.178467 + }, { + "StartTime": 23062.0, + "Position": 444.8687 + }, { + "StartTime": 23125.0, + "Position": 417.069977 + }, { + "StartTime": 23187.0, + "Position": 454.688477 + }, { + "StartTime": 23250.0, + "Position": 428.9612 + }, { + "StartTime": 23312.0, + "Position": 441.92807 + }, { + "StartTime": 23375.0, + "Position": 439.749878 + }, { + "StartTime": 23433.0, + "Position": 455.644684 + }, { + "StartTime": 23491.0, + "Position": 440.7359 + }, { + "StartTime": 23549.0, + "Position": 430.0944 + }, { + "StartTime": 23607.0, + "Position": 420.796173 + }, { + "StartTime": 23665.0, + "Position": 435.897461 + }, { + "StartTime": 23723.0, + "Position": 418.462555 + }, { + "StartTime": 23781.0, + "Position": 405.53775 + }, { + "StartTime": 23874.0, + "Position": 408.720825 + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu new file mode 100644 index 0000000000..40b4409760 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu @@ -0,0 +1,27 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs new file mode 100644 index 0000000000..826c900140 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class CatchBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; + + [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")] + public new void Test(string name) + { + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + if (hitObject is JuiceStream stream) + { + foreach (var nested in stream.NestedHitObjects) + { + yield return new ConvertValue + { + StartTime = nested.StartTime, + Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH + }; + } + } + else + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH + }; + } + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter(); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public float Position; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(Position, other.Position, conversion_lenience); + } +} diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs index 1b348f9c8c..16266196e7 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -15,6 +16,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests { + [TestFixture] public class TestCaseFruitObjects : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs index e2760795c9..2be6dd005d 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Catch.Tests { + [TestFixture] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 894fdc9b45..4e2cdd24c3 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -95,6 +95,7 @@ + @@ -128,6 +129,10 @@ + + + + diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index e14473c478..c8277af415 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// internal class FastRandom { - private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0); + private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; private const uint y = 842502087; private const uint z = 3579807591; @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// Generates a random double value within the range [0, 1). /// /// The random value. - public double NextDouble() => uint_to_real * NextUInt(); + public double NextDouble() => int_to_real * Next(); private uint bitBuffer; private int bitIndex = 32; diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json new file mode 100644 index 0000000000..d593b2b052 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -0,0 +1,103 @@ +{ + "Mappings": [{ + "StartTime": 500, + "Objects": [{ + "StartTime": 500, + "EndTime": 2500, + "Column": 0 + }, + { + "StartTime": 1500, + "EndTime": 2500, + "Column": 1 + } + ] + }, + { + "StartTime": 3000, + "Objects": [{ + "StartTime": 3000, + "EndTime": 4000, + "Column": 2 + }] + }, + { + "StartTime": 4500, + "Objects": [{ + "StartTime": 4500, + "EndTime": 5500, + "Column": 4 + }] + }, + { + "StartTime": 6000, + "Objects": [{ + "StartTime": 6000, + "EndTime": 6500, + "Column": 2 + }] + }, + { + "StartTime": 7000, + "Objects": [{ + "StartTime": 7000, + "EndTime": 8000, + "Column": 2 + }] + }, + { + "StartTime": 8500, + "Objects": [{ + "StartTime": 8500, + "EndTime": 11000, + "Column": 0 + }] + }, + { + "StartTime": 11500, + "Objects": [{ + "StartTime": 11500, + "EndTime": 12000, + "Column": 1 + }] + }, + { + "StartTime": 12500, + "Objects": [{ + "StartTime": 12500, + "EndTime": 16500, + "Column": 4 + }] + }, + { + "StartTime": 17000, + "Objects": [{ + "StartTime": 17000, + "EndTime": 18000, + "Column": 2 + }] + }, + { + "StartTime": 18500, + "Objects": [{ + "StartTime": 18500, + "EndTime": 19450, + "Column": 0 + }] + }, + { + "StartTime": 19875, + "Objects": [{ + "StartTime": 19875, + "EndTime": 23875, + "Column": 1 + }, + { + "StartTime": 19875, + "EndTime": 23875, + "Column": 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu new file mode 100644 index 0000000000..40b4409760 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu @@ -0,0 +1,27 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs new file mode 100644 index 0000000000..2095addc72 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Tests/ManiaBeatmapConversionTest.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class ManiaBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + + private bool isForCurrentRuleset; + + [NonParallelizable] + [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2150")] + public void Test(string name, bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + Column = ((ManiaHitObject)hitObject).Column + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public int Column; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && Column == other.Column; + } +} diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs index 07fb6ac670..1f00b7f32d 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs @@ -9,6 +9,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests { + [TestFixture] public class TestCaseAutoGeneration : OsuTestCase { [Test] diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs index e3aa4c1fd6..3c776a2f4c 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Mania.Tests { + [TestFixture] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index a2e21e2053..a09b3e93a7 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -126,6 +126,7 @@ + @@ -159,6 +160,10 @@ + + + + diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs new file mode 100644 index 0000000000..e0d1b34ca5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection +{ + public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer + { + protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) + { + switch (hitObject) + { + case DrawableHitCircle circle: + return new HitCircleOverlay(circle); + case DrawableSlider slider: + return new SliderOverlay(slider); + } + + return base.CreateOverlayFor(hitObject); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs new file mode 100644 index 0000000000..4e64783840 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class HitCircleOverlay : HitObjectOverlay + { + public HitCircleOverlay(DrawableHitCircle hitCircle) + : base(hitCircle) + { + Origin = Anchor.Centre; + + Position = hitCircle.Position; + Size = hitCircle.Size; + Scale = hitCircle.Scale; + + AddInternal(new RingPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs new file mode 100644 index 0000000000..3c7f8a067b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class SliderCircleOverlay : HitObjectOverlay + { + public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider) + : this(sliderHead, sliderHead.Position, slider) + { + } + + public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider) + : this(sliderTail, sliderTail.Position, slider) + { + } + + private readonly DrawableOsuHitObject hitObject; + + private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) + : base(hitObject) + { + this.hitObject = hitObject; + + Origin = Anchor.Centre; + + Position = position; + Size = slider.HeadCircle.Size; + Scale = slider.HeadCircle.Scale; + + AddInternal(new RingPiece()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + RelativeAnchorPosition = hitObject.RelativeAnchorPosition; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs new file mode 100644 index 0000000000..a035a683e9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays +{ + public class SliderOverlay : HitObjectOverlay + { + private readonly SliderBody body; + private readonly DrawableSlider slider; + + public SliderOverlay(DrawableSlider slider) + : base(slider) + { + this.slider = slider; + + var obj = (Slider)slider.HitObject; + + InternalChildren = new Drawable[] + { + body = new SliderBody(obj) + { + AccentColour = Color4.Transparent, + PathWidth = obj.Scale * 64 + }, + new SliderCircleOverlay(slider.HeadCircle, slider), + new SliderCircleOverlay(slider.TailCircle, slider), + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + body.BorderColour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + Position = slider.Position; + Size = slider.Size; + OriginPosition = slider.OriginPosition; + + // Need to cause one update + body.UpdateProgress(0); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index ae19706da3..70d49a6b4f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; @@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; + + protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index dfbe9ad021..29bf3e248d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; @@ -22,8 +24,14 @@ namespace osu.Game.Rulesets.Osu.Mods if (slider == null) return; + slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y); + slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y); + + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); + var newControlPoints = new List(); - slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y))); + slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y))); slider.ControlPoints = newControlPoints; slider.Curve?.Calculate(); // Recalculate the slider curve diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index beabeb0a19..4aeb76121a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods @@ -25,7 +26,10 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var d in drawables.OfType()) { d.ApplyCustomUpdateState += ApplyHiddenState; + d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier; + foreach (var h in d.HitObject.NestedHitObjects.OfType()) + h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; } } @@ -34,17 +38,20 @@ namespace osu.Game.Rulesets.Osu.Mods if (!(drawable is DrawableOsuHitObject d)) return; - var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.HitObject.TimeFadein; - var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier; + var h = d.HitObject; + + var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein; + var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier; // new duration from completed fade in to end (before fading out) - var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime; + var longFadeDuration = ((h as IHasEndTime)?.EndTime ?? h.StartTime) - fadeOutStartTime; switch (drawable) { case DrawableHitCircle circle: // we don't want to see the approach circle - circle.ApproachCircle.Hide(); + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); // fade out immediately after fade in. using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index db704b0553..79a4714e33 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -78,8 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; List curve = drawableSlider.Body.CurrentCurve; - var positionOnCurve = isRepeatAtEnd ? end : start; - Position = positionOnCurve + drawableSlider.HitObject.StackOffset; + Position = isRepeatAtEnd ? end : start; if (curve.Count < 2) return; @@ -90,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // find the next vector2 in the curve which is not equal to our current position to infer a rotation. for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) { - if (curve[i] == positionOnCurve) + if (curve[i] == Position) continue; - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X)); + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0288e96b3b..f715ed075c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -22,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly List components = new List(); public readonly DrawableHitCircle HeadCircle; + public readonly DrawableSliderTail TailCircle; + public readonly SliderBody Body; public readonly SliderBall Ball; @@ -30,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { slider = s; - DrawableSliderTail tail; + Position = s.StackedPosition; + Container ticks; Container repeatPoints; @@ -39,20 +43,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body = new SliderBody(s) { AccentColour = AccentColour, - Position = s.StackedPosition, PathWidth = s.Scale * 64, }, - ticks = new Container(), - repeatPoints = new Container(), + ticks = new Container { RelativeSizeAxes = Axes.Both }, + repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, Ball = new SliderBall(s) { + BypassAutoSizeAxes = Axes.Both, Scale = new Vector2(s.Scale), AccentColour = AccentColour, AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle), - tail = new DrawableSliderTail(s.TailCircle) + HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.TailCircle.Position - s.Position }, + TailCircle = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position } }; components.Add(Body); @@ -60,15 +64,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddNested(HeadCircle); - AddNested(tail); - components.Add(tail); + AddNested(TailCircle); + components.Add(TailCircle); foreach (var tick in s.NestedHitObjects.OfType()) { - var drawableTick = new DrawableSliderTick(tick) - { - Position = tick.StackedPosition - }; + var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; ticks.Add(drawableTick); components.Add(drawableTick); @@ -77,10 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var repeatPoint in s.NestedHitObjects.OfType()) { - var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) - { - Position = repeatPoint.StackedPosition - }; + var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; repeatPoints.Add(drawableRepeatPoint); components.Add(drawableRepeatPoint); @@ -107,11 +105,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.StackedPositionAt(completionProgress); + HeadCircle.Position = slider.CurvePositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; + + Size = Body.Size; + OriginPosition = Body.PathOffset; + + if (DrawSize != Vector2.Zero) + { + var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = childAnchorPosition; + Ball.RelativeAnchorPosition = childAnchorPosition; + } } protected override void CheckForJudgements(bool userTriggered, double timeOffset) @@ -162,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); - public override Vector2 SelectionPoint => ToScreenSpace(Body.Position); + public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); public override Quad SelectionQuad => Body.PathDrawQuad; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 8835fc2b29..b907aea8c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -19,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderTail(HitCircle hitCircle) : base(hitCircle) { - AlwaysPresent = true; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + AlwaysPresent = true; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 61db10b694..1921c51889 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Game.Rulesets.Objects.Types; using OpenTK; using OpenTK.Graphics; @@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - Position = slider.StackedPositionAt(completionProgress); + Position = slider.CurvePositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index a83ee3a2e1..8c0eb7ff7d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -29,6 +29,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { path.PathWidth = value; } } + /// + /// Offset in absolute coordinates from the start of the curve. + /// + public Vector2 PathOffset { get; private set; } + + public readonly List CurrentCurve = new List(); + public readonly Bindable SnakingIn = new Bindable(); public readonly Bindable SnakingOut = new Bindable(); @@ -75,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private int textureWidth => (int)PathWidth * 2; + private Vector2 topLeftOffset; + private readonly Slider slider; public SliderBody(Slider s) { @@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { container = new BufferedContainer { + RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] { @@ -107,11 +117,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (updateSnaking(p0, p1)) { - // Autosizing does not give us the desired behaviour here. - // We want the container to have the same size as the slider, - // and to be positioned such that the slider head is at (0,0). - container.Size = path.Size; - container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - CurrentCurve[0]); + // The path is generated such that its size encloses it. This change of size causes the path + // to move around while snaking, so we need to offset it to make sure it maintains the + // same position as when it is fully snaked. + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; container.ForceRedraw(); } @@ -121,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load() { reloadTexture(); + computeSize(); } private void reloadTexture() @@ -164,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.Texture = texture; } - public readonly List CurrentCurve = new List(); + private void computeSize() + { + // Generate the entire curve + slider.Curve.GetPathToProgress(CurrentCurve, 0, 1); + foreach (Vector2 p in CurrentCurve) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + PathOffset = path.PositionInBoundingBox(CurrentCurve[0]); + } + private bool updateSnaking(double p0, double p1) { if (SnakedStart == p0 && SnakedEnd == p1) return false; @@ -176,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.ClearVertices(); foreach (Vector2 p in CurrentCurve) - path.AddVertex(p - CurrentCurve[0]); + path.AddVertex(p); return true; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index ce6c88a340..76439ca530 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -3,7 +3,6 @@ using OpenTK; using osu.Game.Rulesets.Objects.Types; -using System; using System.Collections.Generic; using osu.Game.Rulesets.Objects; using System.Linq; @@ -23,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset; - public override Vector2 EndPosition => this.PositionAt(1); + public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); + public override Vector2 EndPosition => Position + this.CurvePositionAt(1); public SliderCurve Curve { get; } = new SliderCurve(); @@ -99,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects HeadCircle = new HitCircle { StartTime = StartTime, - Position = StackedPosition, + Position = Position, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour, Samples = Samples, @@ -109,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle = new HitCircle { StartTime = EndTime, - Position = StackedEndPosition, + Position = EndPosition, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour }; @@ -120,14 +119,16 @@ namespace osu.Game.Rulesets.Osu.Objects private void createTicks() { - if (TickDistance == 0) return; - var length = Curve.Distance; - var tickDistance = Math.Min(TickDistance, length); + var tickDistance = MathHelper.Clamp(TickDistance, 0, length); + + if (tickDistance == 0) return; var minDistanceFromEnd = Velocity * 0.01; - for (var span = 0; span < this.SpanCount(); span++) + var spanCount = this.SpanCount(); + + for (var span = 0; span < spanCount; span++) { var spanStartTime = StartTime + span * SpanDuration; var reversed = span % 2 == 1; @@ -156,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpanIndex = span, SpanStartTime = spanStartTime, StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Curve.PositionAt(distanceProgress), + Position = Position + Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -175,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Objects RepeatIndex = repeatIndex, SpanDuration = SpanDuration, StartTime = StartTime + repeat * SpanDuration, - Position = Curve.PositionAt(repeat % 2), + Position = Position + Curve.PositionAt(repeat % 2), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json new file mode 100644 index 0000000000..b82fddbe79 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -0,0 +1,124 @@ +{ + "Mappings": [{ + "StartTime": 500, + "Objects": [{ + "StartTime": 500, + "EndTime": 2500, + "StartX": 96, + "StartY": 192, + "EndX": 96, + "EndY": 192 + }] + }, + { + "StartTime": 3000, + "Objects": [{ + "StartTime": 3000, + "EndTime": 4000, + "StartX": 256, + "StartY": 192, + "EndX": 256, + "EndY": 192 + }] + }, + { + "StartTime": 4500, + "Objects": [{ + "StartTime": 4500, + "EndTime": 5500, + "StartX": 256, + "StartY": 192, + "EndX": 256, + "EndY": 192 + }] + }, + { + "StartTime": 6000, + "Objects": [{ + "StartTime": 6000, + "EndTime": 6500, + "StartX": 256, + "StartY": 192, + "EndX": 256, + "EndY": 192 + }] + }, + { + "StartTime": 7000, + "Objects": [{ + "StartTime": 7000, + "EndTime": 8000, + "StartX": 256, + "StartY": 128, + "EndX": 256, + "EndY": 128 + }] + }, + { + "StartTime": 8500, + "Objects": [{ + "StartTime": 8500, + "EndTime": 10999, + "StartX": 32, + "StartY": 192, + "EndX": 508.166229, + "EndY": 153.299271 + }] + }, + { + "StartTime": 11500, + "Objects": [{ + "StartTime": 11500, + "EndTime": 12000, + "StartX": 256, + "StartY": 192, + "EndX": 256, + "EndY": 192 + }] + }, + { + "StartTime": 12500, + "Objects": [{ + "StartTime": 12500, + "EndTime": 16500, + "StartX": 512, + "StartY": 320, + "EndX": 291.1977, + "EndY": 40.799427 + }] + }, + { + "StartTime": 17000, + "Objects": [{ + "StartTime": 17000, + "EndTime": 18000, + "StartX": 256, + "StartY": 256, + "EndX": 256, + "EndY": 256 + }] + }, + { + "StartTime": 18500, + "Objects": [{ + "StartTime": 18500, + "EndTime": 19450, + "StartX": 256, + "StartY": 192, + "EndX": 256, + "EndY": 192 + }] + }, + { + "StartTime": 19875, + "Objects": [{ + "StartTime": 19875, + "EndTime": 23874, + "StartX": 216, + "StartY": 231, + "EndX": 408.720825, + "EndY": 339.810455 + }] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu new file mode 100644 index 0000000000..40b4409760 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu @@ -0,0 +1,27 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json new file mode 100644 index 0000000000..7fe038658c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json @@ -0,0 +1,13 @@ +{ + "Mappings": [{ + "StartTime": 118858, + "Objects": [{ + "StartTime": 118858, + "EndTime": 119088, + "StartX": 219, + "StartY": 215, + "EndX": 239.6507, + "EndY": 29.1437378 + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu new file mode 100644 index 0000000000..8c3edc9571 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu @@ -0,0 +1,15 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4.2 +OverallDifficulty:9 +ApproachRate:9.8 +SliderMultiplier:1.87 +SliderTickRate:1 + +[TimingPoints] +49051,230.769230769231,4,2,1,15,1,0 + +[HitObjects] +219,215,118858,2,0,P|224:170|244:-10,1,187,8|2,0:0|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs new file mode 100644 index 0000000000..59c59dc0e3 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class OsuBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; + + [TestCase("basic")] + [TestCase("colinear-perfect-curve")] + public new void Test(string name) + { + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192); + var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition; + + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + StartX = startPosition.X, + StartY = startPosition.Y, + EndX = endPosition.X, + EndY = endPosition.Y + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter(); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const double conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public float StartX; + public float StartY; + public float EndX; + public float EndY; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && Precision.AlmostEquals(StartX, other.StartX) + && Precision.AlmostEquals(StartY, other.StartY, conversion_lenience) + && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) + && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs index d8bb7f88c7..f40d9c05d1 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs @@ -16,10 +16,12 @@ using System.Collections.Generic; using System; using osu.Game.Rulesets.Mods; using System.Linq; +using NUnit.Framework; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseHitCircle : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs index 563df631a1..f030c6db60 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseHitCircleHidden : TestCaseHitCircle { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs index 32339aaf3c..b6dca3f1cb 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index 90a0a450a7..b68f59877b 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -15,6 +15,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Game.Rulesets.Mods; using System.Linq; +using NUnit.Framework; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; @@ -23,6 +24,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseSlider : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] @@ -118,8 +120,8 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-(distance / 2), 0), - new Vector2(distance / 2, 0), + Vector2.Zero, + new Vector2(distance, 0), }, Distance = distance, RepeatCount = repeats, @@ -139,9 +141,9 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(0, 200), - new Vector2(200, 0) + Vector2.Zero, + new Vector2(200, 200), + new Vector2(400, 0) }, Distance = 600, RepeatCount = repeats, @@ -163,12 +165,12 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), + Vector2.Zero, + new Vector2(150, 75), new Vector2(200, 0), - new Vector2(230, 0) + new Vector2(300, -200), + new Vector2(400, 0), + new Vector2(430, 0) }, Distance = 793.4417, RepeatCount = repeats, @@ -190,11 +192,11 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), - new Vector2(230, 0) + Vector2.Zero, + new Vector2(150, 75), + new Vector2(200, 100), + new Vector2(300, -200), + new Vector2(430, 0) }, Distance = 480, RepeatCount = repeats, @@ -216,7 +218,7 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(0, 0), + Vector2.Zero, new Vector2(-200, 0), new Vector2(0, 0), new Vector2(0, -200), @@ -247,10 +249,10 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Catmull, ControlPoints = new List { - new Vector2(-100, 0), - new Vector2(-50, -50), - new Vector2(50, 50), - new Vector2(100, 0) + Vector2.Zero, + new Vector2(50, -50), + new Vector2(150, 50), + new Vector2(200, 0) }, Distance = 300, RepeatCount = repeats, diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs index eba0ebc642..57b719464f 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseSliderHidden : TestCaseSlider { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs index c054f6aa28..d3620bcbda 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -16,6 +17,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseSpinner : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs index a3bbce5b39..75b3b4c763 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Rulesets.Osu.Mods; namespace osu.Game.Rulesets.Osu.Tests { + [TestFixture] public class TestCaseSpinnerHidden : TestCaseSpinner { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 04903d11bf..6c4fe856be 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -64,6 +64,10 @@ + + + + @@ -123,6 +127,7 @@ + @@ -168,6 +173,12 @@ + + + + + + diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 002159439d..cb45ce2dce 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { foreach (var tick in drumRoll.NestedHitObjects.OfType()) { - Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2)); + Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); hitButton = !hitButton; } } @@ -95,16 +95,16 @@ namespace osu.Game.Rulesets.Taiko.Replays if (hit is CentreHit) { if (h.IsStrong) - button = ReplayButtonState.Right1 | ReplayButtonState.Right2; + button = ReplayButtonState.Left1 | ReplayButtonState.Left2; else - button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2; + button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2; } else { if (h.IsStrong) - button = ReplayButtonState.Left1 | ReplayButtonState.Left2; + button = ReplayButtonState.Right1 | ReplayButtonState.Right2; else - button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2; + button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2; } Frames.Add(new TaikoReplayFrame(h.StartTime, button)); diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index 05e10b6fce..1a96b26d34 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Taiko.Replays var actions = new List(); if (CurrentFrame?.MouseRight1 == true) - actions.Add(TaikoAction.LeftCentre); - if (CurrentFrame?.MouseRight2 == true) - actions.Add(TaikoAction.RightCentre); - if (CurrentFrame?.MouseLeft1 == true) actions.Add(TaikoAction.LeftRim); - if (CurrentFrame?.MouseLeft2 == true) + if (CurrentFrame?.MouseRight2 == true) actions.Add(TaikoAction.RightRim); + if (CurrentFrame?.MouseLeft1 == true) + actions.Add(TaikoAction.LeftCentre); + if (CurrentFrame?.MouseLeft2 == true) + actions.Add(TaikoAction.RightCentre); return new List { new ReplayState { PressedActions = actions } }; } diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic-expected-conversion.json new file mode 100644 index 0000000000..5c9310fec7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -0,0 +1,209 @@ +{ + "Mappings": [{ + "StartTime": 500, + "Objects": [{ + "StartTime": 500, + "EndTime": 2499, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 3000, + "Objects": [{ + "StartTime": 3000, + "EndTime": 4000, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": true, + "IsStrong": false + }] + }, + { + "StartTime": 4500, + "Objects": [{ + "StartTime": 4500, + "EndTime": 5500, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": true, + "IsStrong": false + }] + }, + { + "StartTime": 6000, + "Objects": [{ + "StartTime": 6000, + "EndTime": 6500, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": true, + "IsStrong": false + }] + }, + { + "StartTime": 7000, + "Objects": [{ + "StartTime": 7000, + "EndTime": 7000, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 7249, + "EndTime": 7249, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 7499, + "EndTime": 7499, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 7749, + "EndTime": 7749, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 7999, + "EndTime": 7999, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 8500, + "Objects": [{ + "StartTime": 8500, + "EndTime": 10999, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 11500, + "Objects": [{ + "StartTime": 11500, + "EndTime": 12000, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": true, + "IsStrong": false + }] + }, + { + "StartTime": 12500, + "Objects": [{ + "StartTime": 12500, + "EndTime": 16499, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + }, + { + "StartTime": 17000, + "Objects": [{ + "StartTime": 17000, + "EndTime": 17000, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 17249, + "EndTime": 17249, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 17499, + "EndTime": 17499, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 17749, + "EndTime": 17749, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + }, + { + "StartTime": 17999, + "EndTime": 17999, + "IsRim": false, + "IsCentre": true, + "IsDrumRoll": false, + "IsSwell": false, + "IsStrong": false + } + ] + }, + { + "StartTime": 18500, + "Objects": [{ + "StartTime": 18500, + "EndTime": 19450, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": false, + "IsSwell": true, + "IsStrong": false + }] + }, + { + "StartTime": 19875, + "Objects": [{ + "StartTime": 19875, + "EndTime": 23874, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + }] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic.osu new file mode 100644 index 0000000000..40b4409760 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/basic.osu @@ -0,0 +1,27 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs new file mode 100644 index 0000000000..64f728a018 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Tests/TaikoBeatmapConversionTest.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TaikoBeatmapConversionTest : BeatmapConversionTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; + + private bool isForCurrentRuleset; + + [NonParallelizable] + [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] + public void Test(string name, bool isForCurrentRuleset) + { + this.isForCurrentRuleset = isForCurrentRuleset; + base.Test(name); + } + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new ConvertValue + { + StartTime = hitObject.StartTime, + EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, + IsRim = hitObject is RimHit, + IsCentre = hitObject is CentreHit, + IsDrumRoll = hitObject is DrumRoll, + IsSwell = hitObject is Swell, + IsStrong = ((TaikoHitObject)hitObject).IsStrong + }; + } + + protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset); + } + + public struct ConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everwhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public bool IsRim; + public bool IsCentre; + public bool IsDrumRoll; + public bool IsSwell; + public bool IsStrong; + + public bool Equals(ConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && IsRim == other.IsRim + && IsCentre == other.IsCentre + && IsDrumRoll == other.IsDrumRoll + && IsSwell == other.IsSwell + && IsStrong == other.IsStrong; + } +} diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs index 437237661c..80721271d6 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,6 +15,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests { + [TestFixture] public class TestCaseInputDrum : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs index a291bc2eea..f6b0ceb7bd 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Taiko.Tests { + [TestFixture] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index db2db9fff1..07d27455b8 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -112,6 +112,7 @@ + @@ -145,6 +146,10 @@ + + + + diff --git a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs index 912dbc4056..e633d121ca 100644 --- a/osu.Game.Tests/Visual/TestCaseAllPlayers.cs +++ b/osu.Game.Tests/Visual/TestCaseAllPlayers.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseAllPlayers : TestCasePlayer { } diff --git a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs index f081d090c8..66cee634f5 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatSyncedContainer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -17,6 +18,7 @@ using osu.Framework.Lists; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBeatSyncedContainer : OsuTestCase { private readonly MusicController mc; diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index 901d24e531..fe26366362 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -17,6 +18,7 @@ using osu.Game.Screens.Select.Filter; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBeatmapCarousel : OsuTestCase { private TestBeatmapCarousel carousel; @@ -207,6 +209,12 @@ namespace osu.Game.Tests.Visual checkVisibleItemCount(true, 0); AddAssert("Selection is null", () => currentSelection == null); + advanceSelection(true); + AddAssert("Selection is null", () => currentSelection == null); + + advanceSelection(false); + AddAssert("Selection is null", () => currentSelection == null); + AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); AddAssert("Selection is non-null", () => currentSelection != null); diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index bde071c4a3..3ccdaa90d9 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using OpenTK; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -18,6 +19,7 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBeatmapInfoWedge : OsuTestCase { private RulesetStore rulesets; diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs index ad85b3ed52..d9aedb7a5f 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; @@ -12,6 +13,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBeatmapSetOverlay : OsuTestCase { private readonly BeatmapSetOverlay overlay; diff --git a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs index 34abef7d76..20bdd6736c 100644 --- a/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs +++ b/osu.Game.Tests/Visual/TestCaseBreadcrumbs.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBreadcrumbs : OsuTestCase { public TestCaseBreadcrumbs() diff --git a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs index f9ed606080..ae24d86325 100644 --- a/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseBreakOverlay.cs @@ -5,9 +5,11 @@ using osu.Framework.Timing; using osu.Game.Beatmaps.Timing; using osu.Game.Screens.Play.BreaksOverlay; using System.Collections.Generic; +using NUnit.Framework; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseBreakOverlay : OsuTestCase { private readonly BreakOverlay breakOverlay; diff --git a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs index 61da76970e..93740593cb 100644 --- a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs +++ b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; @@ -9,6 +10,7 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseButtonSystem : OsuTestCase { public TestCaseButtonSystem() diff --git a/osu.Game.Tests/Visual/TestCaseChatLink.cs b/osu.Game.Tests/Visual/TestCaseChatLink.cs index 3a7be686e1..786fcb64ab 100644 --- a/osu.Game.Tests/Visual/TestCaseChatLink.cs +++ b/osu.Game.Tests/Visual/TestCaseChatLink.cs @@ -12,12 +12,14 @@ using osu.Game.Users; using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseChatLink : OsuTestCase { private readonly TestChatLineContainer textContainer; diff --git a/osu.Game.Tests/Visual/TestCaseContextMenu.cs b/osu.Game.Tests/Visual/TestCaseContextMenu.cs index 6098187dd6..45c12cf4af 100644 --- a/osu.Game.Tests/Visual/TestCaseContextMenu.cs +++ b/osu.Game.Tests/Visual/TestCaseContextMenu.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -13,6 +14,7 @@ using osu.Game.Graphics.Cursor; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseContextMenu : OsuTestCase { private const int start_time = 0; diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs index 363f6b53f0..72e699c54b 100644 --- a/osu.Game.Tests/Visual/TestCaseCursors.cs +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,6 +17,7 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseCursors : OsuTestCase { private readonly ManualInputManager inputManager; diff --git a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs index d7fbf64664..e9512b29f7 100644 --- a/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseDialogOverlay.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseDialogOverlay : OsuTestCase { public TestCaseDialogOverlay() diff --git a/osu.Game.Tests/Visual/TestCaseDirect.cs b/osu.Game.Tests/Visual/TestCaseDirect.cs index 8fa576135e..3f3dbb0bca 100644 --- a/osu.Game.Tests/Visual/TestCaseDirect.cs +++ b/osu.Game.Tests/Visual/TestCaseDirect.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; @@ -9,6 +10,7 @@ using osu.Game.Rulesets; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseDirect : OsuTestCase { private DirectOverlay direct; diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs index ec70253118..4268fd305e 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,6 +13,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseDrawableRoom : OsuTestCase { private RulesetStore rulesets; diff --git a/osu.Game.Tests/Visual/TestCaseEditor.cs b/osu.Game.Tests/Visual/TestCaseEditor.cs index 37da41c228..c626ca8e7f 100644 --- a/osu.Game.Tests/Visual/TestCaseEditor.cs +++ b/osu.Game.Tests/Visual/TestCaseEditor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Screens.Edit; @@ -10,6 +11,7 @@ using osu.Game.Screens.Edit.Screens; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditor : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 76771ecf82..15bccac172 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -2,12 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorCompose : OsuTestCase { private readonly Random random; diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index 8717f15311..d9850139cd 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorComposeRadioButtons : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index 6a47933a3c..d15ee32d8d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using OpenTK; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorComposeTimeline : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(ScrollableTimeline), typeof(ScrollingTimelineContainer), typeof(BeatmapWaveformGraph), typeof(TimelineButton) }; diff --git a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs index edfcde22b3..ee98fa087a 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; @@ -11,6 +12,7 @@ using osu.Game.Screens.Edit.Menus; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorMenuBar : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(EditorMenuBar), typeof(ScreenSelectionTabControl) }; diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 5e0c0e165c..8d12dfc517 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -3,25 +3,38 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Allocation; using OpenTK; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection; +using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorSelectionLayer : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(SelectionBox), typeof(SelectionLayer), - typeof(CaptureBox) + typeof(CaptureBox), + typeof(HitObjectComposer), + typeof(OsuHitObjectComposer), + typeof(HitObjectOverlayLayer), + typeof(OsuHitObjectOverlayLayer), + typeof(HitObjectOverlay), + typeof(HitCircleOverlay), + typeof(SliderOverlay), + typeof(SliderCircleOverlay) }; [BackgroundDependencyLoader] @@ -35,13 +48,13 @@ namespace osu.Game.Tests.Visual new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, new Slider { + Position = new Vector2(128, 256), ControlPoints = new List { - new Vector2(128, 256), - new Vector2(344, 256), + Vector2.Zero, + new Vector2(216, 0), }, Distance = 400, - Position = new Vector2(128, 256), Velocity = 1, TickDistance = 100, Scale = 0.5f, diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 8c8699fffa..26c8814bc4 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using osu.Framework.Configuration; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseEditorSummaryTimeline : OsuTestCase { private const int length = 60000; diff --git a/osu.Game.Tests/Visual/TestCaseGamefield.cs b/osu.Game.Tests/Visual/TestCaseGamefield.cs index 44f46dea18..80b3f9eb40 100644 --- a/osu.Game.Tests/Visual/TestCaseGamefield.cs +++ b/osu.Game.Tests/Visual/TestCaseGamefield.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseGamefield : OsuTestCase { protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/TestCaseGraph.cs b/osu.Game.Tests/Visual/TestCaseGraph.cs index 99184d4689..285a43707a 100644 --- a/osu.Game.Tests/Visual/TestCaseGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseGraph.cs @@ -2,12 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseGraph : OsuTestCase { public TestCaseGraph() diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index a7fc58f2b5..2e94baa9fc 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -13,6 +14,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseHistoricalSection : OsuTestCase { public override IReadOnlyList RequiredTypes => diff --git a/osu.Game.Tests/Visual/TestCaseIconButton.cs b/osu.Game.Tests/Visual/TestCaseIconButton.cs index 525e867c56..fae79e25bd 100644 --- a/osu.Game.Tests/Visual/TestCaseIconButton.cs +++ b/osu.Game.Tests/Visual/TestCaseIconButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseIconButton : OsuTestCase { public TestCaseIconButton() diff --git a/osu.Game.Tests/Visual/TestCaseIntroSequence.cs b/osu.Game.Tests/Visual/TestCaseIntroSequence.cs index 97116e7746..4af6255b48 100644 --- a/osu.Game.Tests/Visual/TestCaseIntroSequence.cs +++ b/osu.Game.Tests/Visual/TestCaseIntroSequence.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,6 +13,7 @@ using osu.Game.Screens.Menu; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseIntroSequence : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs index 57bb36d144..e39b9f6683 100644 --- a/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs +++ b/osu.Game.Tests/Visual/TestCaseKeyConfiguration.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Game.Overlays; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseKeyConfiguration : OsuTestCase { private readonly KeyBindingOverlay overlay; diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs index ff1b320b5a..bf73c6899b 100644 --- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs +++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.MathUtils; using osu.Game.Screens.Play; @@ -8,6 +9,7 @@ using OpenTK.Input; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseKeyCounter : OsuTestCase { public TestCaseKeyCounter() diff --git a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs index f11c37f5b2..8d91a0f0dd 100644 --- a/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMedalOverlay.cs @@ -3,12 +3,14 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Game.Overlays; using osu.Game.Overlays.MedalSplash; using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseMedalOverlay : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCaseMusicController.cs b/osu.Game.Tests/Visual/TestCaseMusicController.cs index 9424a3fee7..2ddc57d7b4 100644 --- a/osu.Game.Tests/Visual/TestCaseMusicController.cs +++ b/osu.Game.Tests/Visual/TestCaseMusicController.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -11,6 +12,7 @@ using osu.Game.Overlays; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseMusicController : OsuTestCase { private readonly Bindable beatmapBacking = new Bindable(); diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs index b2d3ac8c4d..2ba57f2bd2 100644 --- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -13,6 +14,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseNotificationOverlay : OsuTestCase { private readonly NotificationOverlay manager; diff --git a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs index 9c6c50858f..6fe8bc5a8a 100644 --- a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Game.Overlays; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseOnScreenDisplay : OsuTestCase { private FrameworkConfigManager config; diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs index 9e6776800e..a802db6a10 100644 --- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs +++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; @@ -12,6 +13,7 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseOsuGame : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index a4086ea2cd..d895080afe 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.MathUtils; @@ -20,6 +21,7 @@ using osu.Game.Tests.Platform; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCasePlaySongSelect : OsuTestCase { private BeatmapManager manager; diff --git a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs index 82c0b8f4fd..43e977ba23 100644 --- a/osu.Game.Tests/Visual/TestCasePlaybackControl.cs +++ b/osu.Game.Tests/Visual/TestCasePlaybackControl.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Components; @@ -9,6 +10,7 @@ using OpenTK; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCasePlaybackControl : OsuTestCase { public TestCasePlaybackControl() diff --git a/osu.Game.Tests/Visual/TestCasePopupDialog.cs b/osu.Game.Tests/Visual/TestCasePopupDialog.cs index e3bae3955a..8d830672b7 100644 --- a/osu.Game.Tests/Visual/TestCasePopupDialog.cs +++ b/osu.Game.Tests/Visual/TestCasePopupDialog.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCasePopupDialog : OsuTestCase { public TestCasePopupDialog() diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs index 88631aa982..ad53238e76 100644 --- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs +++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs @@ -9,11 +9,13 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using System.Collections.Generic; using System; +using NUnit.Framework; using osu.Game.Graphics.UserInterface; using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseRankGraph : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs index 595a93b194..a1b683b64c 100644 --- a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; @@ -8,6 +9,7 @@ using osu.Game.Screens.Play.PlayerSettings; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseReplaySettingsOverlay : OsuTestCase { public TestCaseReplaySettingsOverlay() diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs index 012d31e75a..06bdfdb7e1 100644 --- a/osu.Game.Tests/Visual/TestCaseResults.cs +++ b/osu.Game.Tests/Visual/TestCaseResults.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Scoring; @@ -11,6 +12,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseResults : OsuTestCase { private BeatmapManager beatmaps; diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs index 8c4aa02a68..c45312392f 100644 --- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs +++ b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -11,6 +12,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseRoomInspector : OsuTestCase { private RulesetStore rulesets; diff --git a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs index a8dc96ad72..e657035355 100644 --- a/osu.Game.Tests/Visual/TestCaseScoreCounter.cs +++ b/osu.Game.Tests/Visual/TestCaseScoreCounter.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; @@ -10,6 +11,7 @@ using OpenTK; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseScoreCounter : OsuTestCase { public TestCaseScoreCounter() diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index 21d967c3e3..cfa4846939 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; using OpenTK; using osu.Framework.Graphics; @@ -16,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseScrollingHitObjects : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; diff --git a/osu.Game.Tests/Visual/TestCaseSettings.cs b/osu.Game.Tests/Visual/TestCaseSettings.cs index 923ae540db..3f42f2e863 100644 --- a/osu.Game.Tests/Visual/TestCaseSettings.cs +++ b/osu.Game.Tests/Visual/TestCaseSettings.cs @@ -1,12 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseSettings : OsuTestCase { private readonly SettingsOverlay settings; diff --git a/osu.Game.Tests/Visual/TestCaseSkipButton.cs b/osu.Game.Tests/Visual/TestCaseSkipButton.cs index 3fd66f8be3..a4d2019cd7 100644 --- a/osu.Game.Tests/Visual/TestCaseSkipButton.cs +++ b/osu.Game.Tests/Visual/TestCaseSkipButton.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseSkipButton : OsuTestCase { protected override void LoadComplete() diff --git a/osu.Game.Tests/Visual/TestCaseSocial.cs b/osu.Game.Tests/Visual/TestCaseSocial.cs index d3ff18b37f..4003d834d5 100644 --- a/osu.Game.Tests/Visual/TestCaseSocial.cs +++ b/osu.Game.Tests/Visual/TestCaseSocial.cs @@ -3,12 +3,14 @@ using System; using System.Collections.Generic; +using NUnit.Framework; using osu.Game.Overlays; using osu.Game.Overlays.Social; using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseSocial : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCaseSongProgress.cs b/osu.Game.Tests/Visual/TestCaseSongProgress.cs index 2320e8d8db..857fd6c902 100644 --- a/osu.Game.Tests/Visual/TestCaseSongProgress.cs +++ b/osu.Game.Tests/Visual/TestCaseSongProgress.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.MathUtils; using osu.Framework.Timing; @@ -10,6 +11,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseSongProgress : OsuTestCase { private readonly SongProgress progress; diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs index 089733c57e..d34a0e0e5f 100644 --- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs +++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -14,6 +15,7 @@ using OpenTK.Graphics; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseStoryboard : OsuTestCase { private readonly Bindable beatmapBacking = new Bindable(); diff --git a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs index 830dea406a..bf7609ff8d 100644 --- a/osu.Game.Tests/Visual/TestCaseTextAwesome.cs +++ b/osu.Game.Tests/Visual/TestCaseTextAwesome.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -10,6 +11,7 @@ using OpenTK; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseTextAwesome : OsuTestCase { public TestCaseTextAwesome() diff --git a/osu.Game.Tests/Visual/TestCaseToolbar.cs b/osu.Game.Tests/Visual/TestCaseToolbar.cs index b596c4d5e0..94e45fe0c2 100644 --- a/osu.Game.Tests/Visual/TestCaseToolbar.cs +++ b/osu.Game.Tests/Visual/TestCaseToolbar.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Toolbar; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseToolbar : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] diff --git a/osu.Game.Tests/Visual/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/TestCaseUserPanel.cs index b18edf0ccb..ed377dc160 100644 --- a/osu.Game.Tests/Visual/TestCaseUserPanel.cs +++ b/osu.Game.Tests/Visual/TestCaseUserPanel.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Users; @@ -8,6 +9,7 @@ using OpenTK; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseUserPanel : OsuTestCase { public TestCaseUserPanel() diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index 8acc8d1b5b..3caef777e7 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Profile; @@ -11,6 +12,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseUserProfile : OsuTestCase { private readonly TestUserProfileOverlay profile; diff --git a/osu.Game.Tests/Visual/TestCaseUserRanks.cs b/osu.Game.Tests/Visual/TestCaseUserRanks.cs index 1926585f07..effc98c381 100644 --- a/osu.Game.Tests/Visual/TestCaseUserRanks.cs +++ b/osu.Game.Tests/Visual/TestCaseUserRanks.cs @@ -10,9 +10,11 @@ using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Users; using System; using System.Collections.Generic; +using NUnit.Framework; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseUserRanks : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableProfileScore), typeof(RanksSection) }; diff --git a/osu.Game.Tests/Visual/TestCaseWaveform.cs b/osu.Game.Tests/Visual/TestCaseWaveform.cs index 87492e2332..7d4a9d663b 100644 --- a/osu.Game.Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game.Tests/Visual/TestCaseWaveform.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -15,6 +16,7 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Tests.Visual { + [TestFixture] public class TestCaseWaveform : OsuTestCase { private readonly Bindable beatmapBacking = new Bindable(); diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 20de4e9680..711e220b88 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -12,8 +12,16 @@ namespace osu.Game.Beatmaps /// Converts a Beatmap for another mode. /// /// The type of HitObject stored in the Beatmap. - public abstract class BeatmapConverter where T : HitObject + public abstract class BeatmapConverter : IBeatmapConverter + where T : HitObject { + private event Action> ObjectConverted; + event Action> IBeatmapConverter.ObjectConverted + { + add => ObjectConverted += value; + remove => ObjectConverted -= value; + } + /// /// Checks if a Beatmap can be converted using this Beatmap Converter. /// @@ -32,6 +40,8 @@ namespace osu.Game.Beatmaps return ConvertBeatmap(new Beatmap(original)); } + void IBeatmapConverter.Convert(Beatmap original) => Convert(original); + /// /// Performs the conversion of a Beatmap using this Beatmap Converter. /// @@ -63,8 +73,11 @@ namespace osu.Game.Beatmaps yield break; } + var converted = ConvertHitObject(original, beatmap).ToList(); + ObjectConverted?.Invoke(original, converted); + // Convert the hit object - foreach (var obj in ConvertHitObject(original, beatmap)) + foreach (var obj in converted) { if (obj == null) continue; diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 69027ffd73..2b42553891 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; + namespace osu.Game.Beatmaps.ControlPoints { public class DifficultyControlPoint : ControlPoint @@ -8,6 +10,12 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The speed multiplier at this control point. /// - public double SpeedMultiplier = 1; + public double SpeedMultiplier + { + get => speedMultiplier; + set => speedMultiplier = MathHelper.Clamp(value, 0.1, 10); + } + + private double speedMultiplier = 1; } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 0592ef38c5..0db1f08a90 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using osu.Game.Beatmaps.Timing; namespace osu.Game.Beatmaps.ControlPoints @@ -15,6 +16,12 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The beat length at this control point. /// - public double BeatLength = 1000; + public double BeatLength + { + get => beatLength; + set => beatLength = MathHelper.Clamp(value, 6, 60000); + } + + private double beatLength = 1000; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 3847787a4c..3e7b36f324 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -42,6 +42,10 @@ namespace osu.Game.Beatmaps.Formats ParseContent(stream); + // objects may be out of order *only* if a user has manually edited an .osu file. + // unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828). + this.beatmap.HitObjects.Sort((x, y) => x.StartTime.CompareTo(y.StartTime)); + foreach (var hitObject in this.beatmap.HitObjects) hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } diff --git a/osu.Game/Beatmaps/IBeatmapConverter.cs b/osu.Game/Beatmaps/IBeatmapConverter.cs new file mode 100644 index 0000000000..ebd900b97e --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapConverter.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Beatmaps +{ + public interface IBeatmapConverter + { + /// + /// Invoked when a has been converted. + /// The first argument contains the that was converted. + /// The second argument contains the s that were output from the conversion process. + /// + event Action> ObjectConverted; + + /// + /// Converts a Beatmap using this Beatmap Converter. + /// + /// The un-converted Beatmap. + void Convert(Beatmap beatmap); + } +} diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index cb894ca382..f4400b7df2 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -8,12 +8,15 @@ using OpenTK; using osu.Framework.Allocation; using osu.Game.Configuration; using osu.Framework.Configuration; +using osu.Framework.MathUtils; namespace osu.Game.Graphics.Containers { public class ParallaxContainer : Container, IRequireHighFrequencyMousePosition { - public float ParallaxAmount = 0.02f; + public const float DEFAULT_PARALLAX_AMOUNT = 0.02f; + + public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT; private Bindable parallaxEnabled; @@ -61,8 +64,9 @@ namespace osu.Game.Graphics.Containers if (parallaxEnabled) { - Vector2 offset = input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2; - content.MoveTo(offset * ParallaxAmount, firstUpdate ? 0 : 1000, Easing.OutQuint); + Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; + + content.Position = Interpolation.ValueAt(Clock.ElapsedFrameTime, content.Position, offset, 0, 1000, Easing.OutQuint); content.Scale = new Vector2(1 + ParallaxAmount); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 95eb88c5c8..aeb23dccd7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -20,6 +20,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Framework.Threading; @@ -414,6 +415,7 @@ namespace osu.Game sensitivity.Disabled = true; frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + frameworkConfig.GetBindable(FrameworkSetting.ConfineMouseMode).SetDefault(); return true; case GlobalAction.ToggleToolbar: Toolbar.ToggleVisibility(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d7268fb186..d0a507be98 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -65,6 +65,14 @@ namespace osu.Game.Overlays.Mods Ruleset.TriggerChange(); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + Ruleset.UnbindAll(); + SelectedMods.UnbindAll(); + } + private void selectedModsChanged(IEnumerable obj) { foreach (ModSection section in ModSectionsContainer.Children) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1246127257..e6a51cc39b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,14 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Layers; using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.UI; @@ -50,13 +49,25 @@ namespace osu.Game.Rulesets.Edit return; } - ScalableContainer createLayerContainerWithContent(Drawable content) + HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer(); + SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield); + + var layerBelowRuleset = new BorderLayer { - var container = CreateLayerContainer(); - container.Child = content; - layerContainers.Add(container); - return container; - } + RelativeSizeAxes = Axes.Both, + Child = CreateLayerContainer() + }; + + var layerAboveRuleset = CreateLayerContainer(); + layerAboveRuleset.Children = new Drawable[] + { + selectionLayer, // Below object overlays for input + hitObjectOverlayLayer, + selectionLayer.CreateProxy() // Proxy above object overlays for selections + }; + + layerContainers.Add(layerBelowRuleset); + layerContainers.Add(layerAboveRuleset); RadioButtonCollection toolboxCollection; InternalChild = new GridContainer @@ -82,17 +93,9 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - createLayerContainerWithContent(new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, - Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } - }), + layerBelowRuleset, rulesetContainer, - createLayerContainerWithContent(new SelectionLayer(rulesetContainer.Playfield)) + layerAboveRuleset } } }, @@ -103,6 +106,9 @@ namespace osu.Game.Rulesets.Edit } }; + selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay; + selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay; + toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( @@ -136,5 +142,10 @@ namespace osu.Game.Rulesets.Edit /// Creates a which provides a layer above or below the . /// protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; + + /// + /// Creates the which overlays selected s. + /// + protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer(); } } diff --git a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs b/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs new file mode 100644 index 0000000000..54c30b8d89 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Edit.Layers +{ + public class BorderLayer : Container + { + protected override Container Content => content; + private readonly Container content; + + public BorderLayer() + { + InternalChildren = new Drawable[] + { + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }, + content = new Container { RelativeSizeAxes = Axes.Both } + }; + } + } +} diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs new file mode 100644 index 0000000000..543dd2cc54 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Edit.Layers.Selection +{ + public class HitObjectOverlay : OverlayContainer + { + // ReSharper disable once NotAccessedField.Local + // This will be used later to handle drag movement, etc + private readonly DrawableHitObject hitObject; + + public HitObjectOverlay(DrawableHitObject hitObject) + { + this.hitObject = hitObject; + + State = Visibility.Visible; + } + + protected override void PopIn() => Alpha = 1; + protected override void PopOut() => Alpha = 0; + } +} diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs new file mode 100644 index 0000000000..0b6e63d1fe --- /dev/null +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Edit.Layers.Selection +{ + public class HitObjectOverlayLayer : CompositeDrawable + { + private readonly Dictionary existingOverlays = new Dictionary(); + + public HitObjectOverlayLayer() + { + RelativeSizeAxes = Axes.Both; + } + + /// + /// Adds an overlay for a which adds movement support. + /// + /// The to create an overlay for. + public void AddOverlay(DrawableHitObject hitObject) + { + var overlay = CreateOverlayFor(hitObject); + if (overlay == null) + return; + + existingOverlays[hitObject] = overlay; + AddInternal(overlay); + } + + /// + /// Removes the overlay for a . + /// + /// The to remove the overlay for. + public void RemoveOverlay(DrawableHitObject hitObject) + { + if (!existingOverlays.TryGetValue(hitObject, out var existing)) + return; + + existing.Hide(); + existing.Expire(); + } + + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null; + } +} diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index bda613f617..3895d34d7f 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -1,8 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -15,6 +17,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection { public class SelectionLayer : CompositeDrawable { + /// + /// Invoked when a is selected. + /// + public event Action ObjectSelected; + + /// + /// Invoked when a is deselected. + /// + public event Action ObjectDeselected; + private readonly Playfield playfield; public SelectionLayer(Playfield playfield) @@ -27,11 +39,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection private SelectionBox selectionBox; private CaptureBox captureBox; - private readonly List selectedHitObjects = new List(); + private readonly HashSet selectedHitObjects = new HashSet(); protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - clearSelection(); + DeselectAll(); return true; } @@ -74,24 +86,85 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection return true; } + /// + /// Selects a . + /// + /// The to select. + public void Select(DrawableHitObject hitObject) + { + if (!select(hitObject)) + return; + + clearCapture(); + finishSelection(); + } + + /// + /// Selects a without performing capture updates. + /// + /// The to select. + /// Whether was selected. + private bool select(DrawableHitObject hitObject) + { + if (!selectedHitObjects.Add(hitObject)) + return false; + + ObjectSelected?.Invoke(hitObject); + return true; + } + + /// + /// Deselects a . + /// + /// The to deselect. + public void Deselect(DrawableHitObject hitObject) + { + if (!deselect(hitObject)) + return; + + clearCapture(); + finishSelection(); + } + + /// + /// Deselects a without performing capture updates. + /// + /// The to deselect. + /// Whether the was deselected. + private bool deselect(DrawableHitObject hitObject) + { + if (!selectedHitObjects.Remove(hitObject)) + return false; + + ObjectDeselected?.Invoke(hitObject); + return true; + } + /// /// Deselects all selected s. /// - private void clearSelection() + public void DeselectAll() { + selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h)); selectedHitObjects.Clear(); - captureBox?.Hide(); - captureBox?.Expire(); + + clearCapture(); } /// /// Selects all hitobjects that are present within the area of a . /// /// The selection . + // Todo: If needed we can severely reduce allocations in this method private void selectQuad(Quad screenSpaceQuad) { - foreach (var obj in playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint))) - selectedHitObjects.Add(obj); + var expectedSelection = playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint)).ToList(); + + var toRemove = selectedHitObjects.Except(expectedSelection).ToList(); + foreach (var obj in toRemove) + deselect(obj); + + expectedSelection.ForEach(h => select(h)); } /// @@ -100,11 +173,17 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// The to select at. private void selectPoint(Vector2 screenSpacePoint) { - var selected = playfield.HitObjects.Objects.Reverse().Where(h => h.IsAlive && h.IsPresent).FirstOrDefault(h => h.ReceiveMouseInputAt(screenSpacePoint)); - if (selected == null) + var target = playfield.HitObjects.Objects.Reverse().Where(h => h.IsAlive && h.IsPresent).FirstOrDefault(h => h.ReceiveMouseInputAt(screenSpacePoint)); + if (target == null) return; - selectedHitObjects.Add(selected); + select(target); + } + + private void clearCapture() + { + captureBox?.Hide(); + captureBox?.Expire(); } private void finishSelection() diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 5fdc9a07e1..ce292ef223 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -9,6 +9,7 @@ using System.Globalization; using osu.Game.Beatmaps.Formats; using osu.Game.Audio; using System.Linq; +using osu.Framework.MathUtils; namespace osu.Game.Rulesets.Objects.Legacy { @@ -41,9 +42,11 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if ((type & ConvertHitObjectType.Slider) > 0) { + var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1])); + CurveType curveType = CurveType.Catmull; double length = 0; - var points = new List { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; + var points = new List { Vector2.Zero }; string[] pointsplit = split[5].Split('|'); foreach (string t in pointsplit) @@ -69,9 +72,14 @@ namespace osu.Game.Rulesets.Objects.Legacy } string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture))); + points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); } + // osu-stable special-cased colinear perfect curves to a CurveType.Linear + bool isLinear(List p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + if (points.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) + curveType = CurveType.Linear; + int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); if (repeatCount > 9000) @@ -134,7 +142,7 @@ namespace osu.Game.Rulesets.Objects.Legacy for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples); + result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples); } else if ((type & ConvertHitObjectType.Spinner) > 0) { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 19f9a93976..d2a0530dd9 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK; using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; @@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu Position = position, NewCombo = newCombo, ControlPoints = controlPoints, - Distance = length, + Distance = Math.Max(0, length), CurveType = curveType, RepeatSamples = repeatSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index c03bdb240e..251ad3e3cd 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// The position on the curve. - public static Vector2 PositionAt(this IHasCurve obj, double progress) + public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index 43ed770f77..9762828e7d 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.UI }); } - public class ScaledContainer : Container + private class ScaledContainer : Container { /// /// The value to scale the width of the content to match. diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index b91ff0d74b..3fcb885655 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Menu Alpha = 0.5f, Size = new Vector2(0.96f) }, - new BufferedContainer + new Container { AutoSizeAxes = Axes.Both, Children = new Drawable[] diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index a2d41dc206..8f4e08d4a2 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -52,6 +52,10 @@ namespace osu.Game.Screens protected readonly Bindable Beatmap = new Bindable(); + protected virtual float BackgroundParallaxAmount => 1; + + private ParallaxContainer backgroundParallaxContainer; + public WorkingBeatmap InitialBeatmap { set @@ -102,11 +106,10 @@ namespace osu.Game.Screens protected override void OnResuming(Screen last) { - base.OnResuming(last); - logo.AppendAnimatingAction(() => LogoArriving(logo, true), true); sampleExit?.Play(); + applyArrivingDefaults(true); - ShowOverlays.Value = ShowOverlaysOnEnter; + base.OnResuming(last); } protected override void OnSuspending(Screen next) @@ -123,6 +126,8 @@ namespace osu.Game.Screens if (lastOsu?.Background != null) { + backgroundParallaxContainer = lastOsu.backgroundParallaxContainer; + if (bg == null || lastOsu.Background.Equals(bg)) //we can keep the previous mode's background. Background = lastOsu.Background; @@ -136,7 +141,7 @@ namespace osu.Game.Screens // this makes up for the fact our padding changes when the global toolbar is visible. bg.Scale = new Vector2(1.06f); - AddInternal(new ParallaxContainer + AddInternal(backgroundParallaxContainer = new ParallaxContainer { Depth = float.MaxValue, Children = new[] @@ -149,11 +154,9 @@ namespace osu.Game.Screens if ((logo = lastOsu?.logo) == null) LoadComponentAsync(logo = new OsuLogo { Alpha = 0 }, AddInternal); - logo.AppendAnimatingAction(() => LogoArriving(logo, false), true); + applyArrivingDefaults(false); base.OnEntering(last); - - ShowOverlays.Value = ShowOverlaysOnEnter; } protected override bool OnExiting(Screen next) @@ -193,6 +196,16 @@ namespace osu.Game.Screens logo.Ripple = true; } + private void applyArrivingDefaults(bool isResuming) + { + logo.AppendAnimatingAction(() => LogoArriving(logo, isResuming), true); + + if (backgroundParallaxContainer != null) + backgroundParallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * BackgroundParallaxAmount; + + ShowOverlays.Value = ShowOverlaysOnEnter; + } + private void onExitingLogo() { logo.AppendAnimatingAction(() => { LogoExiting(logo); }, false); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4954618ef9..7a0c723ab5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); + protected override float BackgroundParallaxAmount => 0.1f; + public override bool ShowOverlaysOnEnter => false; public Action RestartRequested; @@ -351,7 +353,7 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { - if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false) && (!pauseContainer?.IsResuming ?? false)) + if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false) && (!pauseContainer?.IsResuming ?? true)) { // In the case of replays, we may have changed the playback rate. applyRateFromMods(); diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index ffe7ae04f8..4f5cc79b53 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Play { var xFill = value * UsableWidth; fill.Width = xFill; - handleBase.MoveToX(xFill); + handleBase.X = xFill; } protected override void OnUserChange() => OnSeek?.Invoke(Current); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 6a6042d7d4..9793440348 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -192,7 +192,9 @@ namespace osu.Game.Screens.Select /// Whether to skip individual difficulties and only increment over full groups. public void SelectNext(int direction = 1, bool skipDifficulties = true) { - if (!Items.Any()) + var visibleItems = Items.Where(s => !s.Item.Filtered).ToList(); + + if (!visibleItems.Any()) return; DrawableCarouselItem drawable = null; @@ -202,15 +204,15 @@ namespace osu.Game.Screens.Select // we can fix this by changing this method to not reference drawables / Items in the first place. return; - int originalIndex = Items.IndexOf(drawable); + int originalIndex = visibleItems.IndexOf(drawable); int currentIndex = originalIndex; // local function to increment the index in the required direction, wrapping over extremities. - int incrementIndex() => currentIndex = (currentIndex + direction + Items.Count) % Items.Count; + int incrementIndex() => currentIndex = (currentIndex + direction + visibleItems.Count) % visibleItems.Count; while (incrementIndex() != originalIndex) { - var item = Items[currentIndex].Item; + var item = visibleItems[currentIndex].Item; if (item.Filtered || item.State == CarouselItemState.Selected) continue; @@ -407,12 +409,14 @@ namespace osu.Game.Screens.Select continue; } + float depth = i + (item is DrawableCarouselBeatmapSet ? -Items.Count : 0); + // Only add if we're not already part of the content. if (!scrollableContent.Contains(item)) { // Makes sure headers are always _below_ items, // and depth flows downward. - item.Depth = i + (item is DrawableCarouselBeatmapSet ? -Items.Count : 0); + item.Depth = depth; switch (item.LoadState) { @@ -426,6 +430,10 @@ namespace osu.Game.Screens.Select break; } } + else + { + scrollableContent.ChangeChildDepth(item, depth); + } } // this is not actually useful right now, but once we have groups may well be. diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 739bc39269..c347bfe70f 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using System.Linq; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; @@ -47,13 +49,15 @@ namespace osu.Game.Screens.Select private SampleChannel sampleConfirm; - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame game) - { - sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); + public readonly Bindable> SelectedMods = new Bindable>(new List()); - if (game != null) - modSelect.SelectedMods.BindTo(game.SelectedMods); + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame osu) + { + if (osu != null) SelectedMods.BindTo(osu.SelectedMods); + modSelect.SelectedMods.BindTo(SelectedMods); + + sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue); @@ -80,7 +84,7 @@ namespace osu.Game.Screens.Select { base.UpdateBeatmap(beatmap); - beatmap.Mods.BindTo(modSelect.SelectedMods); + beatmap.Mods.BindTo(SelectedMods); BeatmapDetails.Beatmap = beatmap; @@ -95,7 +99,7 @@ namespace osu.Game.Screens.Select if (removeAutoModOnResume) { var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod().GetType(); - modSelect.SelectedMods.Value = modSelect.SelectedMods.Value.Where(m => m.GetType() != autoType).ToArray(); + SelectedMods.Value = SelectedMods.Value.Where(m => m.GetType() != autoType).ToArray(); removeAutoModOnResume = false; } @@ -125,7 +129,7 @@ namespace osu.Game.Screens.Select if (Beatmap.Value.Track != null) Beatmap.Value.Track.Looping = false; - Beatmap.Value.Mods.UnbindBindings(); + SelectedMods.UnbindAll(); Beatmap.Value.Mods.Value = new Mod[] { }; return false; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6e1d95d42e..8033f8da8b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -397,6 +397,8 @@ namespace osu.Game.Screens.Select protected override bool OnExiting(Screen next) { + FinaliseSelection(); + beatmapInfoWedge.State = Visibility.Hidden; Content.FadeOut(100); diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs new file mode 100644 index 0000000000..596dbe84ba --- /dev/null +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -0,0 +1,141 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public abstract class BeatmapConversionTest + where TConvertValue : IEquatable + { + private const string resource_namespace = "Testing.Beatmaps"; + private const string expected_conversion_suffix = "-expected-conversion"; + + protected abstract string ResourceAssembly { get; } + + protected void Test(string name) + { + var ourResult = convert(name); + var expectedResult = read(name); + + Assert.Multiple(() => + { + int mappingCounter = 0; + while (true) + { + if (mappingCounter >= ourResult.Mappings.Count && mappingCounter >= expectedResult.Mappings.Count) + break; + if (mappingCounter >= ourResult.Mappings.Count) + Assert.Fail($"A conversion did not generate any hitobjects, but should have, for hitobject at time: {expectedResult.Mappings[mappingCounter].StartTime}\n"); + else if (mappingCounter >= expectedResult.Mappings.Count) + Assert.Fail($"A conversion generated hitobjects, but should not have, for hitobject at time: {ourResult.Mappings[mappingCounter].StartTime}\n"); + else + { + var counter = mappingCounter; + Assert.Multiple(() => + { + var ourMapping = ourResult.Mappings[counter]; + var expectedMapping = expectedResult.Mappings[counter]; + + int objectCounter = 0; + while (true) + { + if (objectCounter >= ourMapping.Objects.Count && objectCounter >= expectedMapping.Objects.Count) + break; + if (objectCounter >= ourMapping.Objects.Count) + Assert.Fail($"The conversion did not generate a hitobject, but should have, for hitobject at time: {expectedMapping.StartTime}:\n" + + $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"); + else if (objectCounter >= expectedMapping.Objects.Count) + Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n" + + $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n"); + else if (!EqualityComparer.Default.Equals(expectedMapping.Objects[objectCounter], ourMapping.Objects[objectCounter])) + { + Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}\n" + + $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n" + + $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n"); + } + + objectCounter++; + } + }); + } + + mappingCounter++; + } + }); + } + + private ConvertResult convert(string name) + { + var beatmap = getBeatmap(name); + + var result = new ConvertResult(); + + var converter = CreateConverter(beatmap); + converter.ObjectConverted += (orig, converted) => + { + converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty)); + + var mapping = new ConvertMapping { StartTime = orig.StartTime }; + foreach (var obj in converted) + mapping.Objects.AddRange(CreateConvertValue(obj)); + result.Mappings.Add(mapping); + }; + + converter.Convert(beatmap); + + return result; + } + + private ConvertResult read(string name) + { + using (var resStream = openResource($"{resource_namespace}.{name}{expected_conversion_suffix}.json")) + using (var reader = new StreamReader(resStream)) + { + var contents = reader.ReadToEnd(); + return JsonConvert.DeserializeObject(contents); + } + } + + private Beatmap getBeatmap(string name) + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = openResource($"{resource_namespace}.{name}.osu")) + using (var stream = new StreamReader(resStream)) + return decoder.DecodeBeatmap(stream); + } + + private Stream openResource(string name) + { + var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); + return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); + } + + protected abstract IEnumerable CreateConvertValue(HitObject hitObject); + protected abstract IBeatmapConverter CreateConverter(Beatmap beatmap); + + private class ConvertMapping + { + [JsonProperty] + public double StartTime; + [JsonProperty] + public List Objects = new List(); + } + + private class ConvertResult + { + [JsonProperty] + public List Mappings = new List(); + } + } +} diff --git a/osu.Game/Tests/TestTestCase.cs b/osu.Game/Tests/TestTestCase.cs new file mode 100644 index 0000000000..4efd57095e --- /dev/null +++ b/osu.Game/Tests/TestTestCase.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Testing; + +namespace osu.Game.Tests +{ + [TestFixture] + internal class TestTestCase : TestCase + { + // This TestCase is required for nunit to not throw errors + // See: https://github.com/nunit/nunit/issues/1118 + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6a06bf540b..ff365ad93e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -270,6 +270,7 @@ + @@ -352,7 +353,10 @@ + + + @@ -880,9 +884,11 @@ + + @@ -936,4 +942,4 @@ - + \ No newline at end of file