diff --git a/build/cakebuild.csproj b/build/cakebuild.csproj
index 8ccce35e26..d5a6d44740 100644
--- a/build/cakebuild.csproj
+++ b/build/cakebuild.csproj
@@ -5,7 +5,7 @@
netcoreapp2.0
-
-
+
+
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 8c9e1f279f..538aaf2d7a 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -28,8 +28,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
index 6a6d0abe24..392170f0a8 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
@@ -21,10 +21,9 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase("basic")]
[TestCase("colinear-perfect-curve")]
[TestCase("slider-ticks")]
- public void Test(string name)
- {
- base.Test(name);
- }
+ [TestCase("repeat-slider")]
+ [TestCase("uneven-repeat-slider")]
+ public void Test(string name) => base.Test(name);
protected override IEnumerable CreateConvertValue(HitObject hitObject)
{
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json
new file mode 100644
index 0000000000..fb4050963f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json
@@ -0,0 +1,222 @@
+{
+ "Mappings": [{
+ "StartTime": 369,
+ "Objects": [{
+ "StartTime": 369,
+ "EndTime": 369,
+ "X": 177,
+ "Y": 191
+ },
+ {
+ "StartTime": 450,
+ "EndTime": 450,
+ "X": 216.539276,
+ "Y": 191.192871
+ },
+ {
+ "StartTime": 532,
+ "EndTime": 532,
+ "X": 256.5667,
+ "Y": 191.388138
+ },
+ {
+ "StartTime": 614,
+ "EndTime": 614,
+ "X": 296.594116,
+ "Y": 191.583389
+ },
+ {
+ "StartTime": 696,
+ "EndTime": 696,
+ "X": 336.621521,
+ "Y": 191.778641
+ },
+ {
+ "StartTime": 778,
+ "EndTime": 778,
+ "X": 376.648926,
+ "Y": 191.9739
+ },
+ {
+ "StartTime": 860,
+ "EndTime": 860,
+ "X": 337.318878,
+ "Y": 191.782043
+ },
+ {
+ "StartTime": 942,
+ "EndTime": 942,
+ "X": 297.291443,
+ "Y": 191.586792
+ },
+ {
+ "StartTime": 1024,
+ "EndTime": 1024,
+ "X": 257.264038,
+ "Y": 191.391541
+ },
+ {
+ "StartTime": 1106,
+ "EndTime": 1106,
+ "X": 217.2366,
+ "Y": 191.196274
+ },
+ {
+ "StartTime": 1188,
+ "EndTime": 1188,
+ "X": 177.209213,
+ "Y": 191.001022
+ },
+ {
+ "StartTime": 1270,
+ "EndTime": 1270,
+ "X": 216.818192,
+ "Y": 191.194229
+ },
+ {
+ "StartTime": 1352,
+ "EndTime": 1352,
+ "X": 256.8456,
+ "Y": 191.3895
+ },
+ {
+ "StartTime": 1434,
+ "EndTime": 1434,
+ "X": 296.873047,
+ "Y": 191.584747
+ },
+ {
+ "StartTime": 1516,
+ "EndTime": 1516,
+ "X": 336.900452,
+ "Y": 191.78
+ },
+ {
+ "StartTime": 1598,
+ "EndTime": 1598,
+ "X": 376.927917,
+ "Y": 191.975266
+ },
+ {
+ "StartTime": 1680,
+ "EndTime": 1680,
+ "X": 337.039948,
+ "Y": 191.780685
+ },
+ {
+ "StartTime": 1762,
+ "EndTime": 1762,
+ "X": 297.0125,
+ "Y": 191.585434
+ },
+ {
+ "StartTime": 1844,
+ "EndTime": 1844,
+ "X": 256.9851,
+ "Y": 191.390167
+ },
+ {
+ "StartTime": 1926,
+ "EndTime": 1926,
+ "X": 216.957672,
+ "Y": 191.194916
+ },
+ {
+ "StartTime": 2008,
+ "EndTime": 2008,
+ "X": 177.069717,
+ "Y": 191.000336
+ },
+ {
+ "StartTime": 2090,
+ "EndTime": 2090,
+ "X": 217.097137,
+ "Y": 191.1956
+ },
+ {
+ "StartTime": 2172,
+ "EndTime": 2172,
+ "X": 257.124573,
+ "Y": 191.390854
+ },
+ {
+ "StartTime": 2254,
+ "EndTime": 2254,
+ "X": 297.152,
+ "Y": 191.5861
+ },
+ {
+ "StartTime": 2336,
+ "EndTime": 2336,
+ "X": 337.179443,
+ "Y": 191.781372
+ },
+ {
+ "StartTime": 2418,
+ "EndTime": 2418,
+ "X": 376.7884,
+ "Y": 191.974579
+ },
+ {
+ "StartTime": 2500,
+ "EndTime": 2500,
+ "X": 336.760956,
+ "Y": 191.779327
+ },
+ {
+ "StartTime": 2582,
+ "EndTime": 2582,
+ "X": 296.733643,
+ "Y": 191.584076
+ },
+ {
+ "StartTime": 2664,
+ "EndTime": 2664,
+ "X": 256.7062,
+ "Y": 191.388809
+ },
+ {
+ "StartTime": 2746,
+ "EndTime": 2746,
+ "X": 216.678772,
+ "Y": 191.193558
+ },
+ {
+ "StartTime": 2828,
+ "EndTime": 2828,
+ "X": 177.348663,
+ "Y": 191.0017
+ },
+ {
+ "StartTime": 2909,
+ "EndTime": 2909,
+ "X": 216.887909,
+ "Y": 191.19458
+ },
+ {
+ "StartTime": 2991,
+ "EndTime": 2991,
+ "X": 256.915344,
+ "Y": 191.389832
+ },
+ {
+ "StartTime": 3073,
+ "EndTime": 3073,
+ "X": 296.942749,
+ "Y": 191.585083
+ },
+ {
+ "StartTime": 3155,
+ "EndTime": 3155,
+ "X": 336.970184,
+ "Y": 191.78035
+ },
+ {
+ "StartTime": 3201,
+ "EndTime": 3201,
+ "X": 376.99762,
+ "Y": 191.9756
+ }
+ ]
+ }]
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu
new file mode 100644
index 0000000000..624d905384
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu
@@ -0,0 +1,18 @@
+osu file format v14
+
+[General]
+StackLeniency: 0.4
+Mode: 0
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8
+SliderMultiplier:1.6
+SliderTickRate:4
+
+[TimingPoints]
+369,327.868852459016,4,2,2,32,1,0
+
+[HitObjects]
+177,191,369,6,0,L|382:192,7,200
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json
new file mode 100644
index 0000000000..12d1645c04
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json
@@ -0,0 +1,348 @@
+{
+ "Mappings": [{
+ "StartTime": 369,
+ "Objects": [{
+ "StartTime": 369,
+ "EndTime": 369,
+ "X": 127,
+ "Y": 194
+ },
+ {
+ "StartTime": 450,
+ "EndTime": 450,
+ "X": 166.53389,
+ "Y": 193.8691
+ },
+ {
+ "StartTime": 532,
+ "EndTime": 532,
+ "X": 206.555847,
+ "Y": 193.736572
+ },
+ {
+ "StartTime": 614,
+ "EndTime": 614,
+ "X": 246.57782,
+ "Y": 193.60405
+ },
+ {
+ "StartTime": 696,
+ "EndTime": 696,
+ "X": 286.5998,
+ "Y": 193.471527
+ },
+ {
+ "StartTime": 778,
+ "EndTime": 778,
+ "X": 326.621765,
+ "Y": 193.339
+ },
+ {
+ "StartTime": 860,
+ "EndTime": 860,
+ "X": 366.6437,
+ "Y": 193.206482
+ },
+ {
+ "StartTime": 942,
+ "EndTime": 942,
+ "X": 406.66568,
+ "Y": 193.073959
+ },
+ {
+ "StartTime": 970,
+ "EndTime": 970,
+ "X": 420.331726,
+ "Y": 193.0287
+ },
+ {
+ "StartTime": 997,
+ "EndTime": 997,
+ "X": 407.153748,
+ "Y": 193.072342
+ },
+ {
+ "StartTime": 1079,
+ "EndTime": 1079,
+ "X": 367.131775,
+ "Y": 193.204865
+ },
+ {
+ "StartTime": 1161,
+ "EndTime": 1161,
+ "X": 327.1098,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 1243,
+ "EndTime": 1243,
+ "X": 287.08783,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 1325,
+ "EndTime": 1325,
+ "X": 247.0659,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 1407,
+ "EndTime": 1407,
+ "X": 207.043915,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 1489,
+ "EndTime": 1489,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 1571,
+ "EndTime": 1571,
+ "X": 127,
+ "Y": 194
+ },
+ {
+ "StartTime": 1653,
+ "EndTime": 1653,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 1735,
+ "EndTime": 1735,
+ "X": 207.043976,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 1817,
+ "EndTime": 1817,
+ "X": 247.065887,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 1899,
+ "EndTime": 1899,
+ "X": 287.08783,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 1981,
+ "EndTime": 1981,
+ "X": 327.1098,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 2062,
+ "EndTime": 2062,
+ "X": 366.643738,
+ "Y": 193.206482
+ },
+ {
+ "StartTime": 2144,
+ "EndTime": 2144,
+ "X": 406.665649,
+ "Y": 193.073959
+ },
+ {
+ "StartTime": 2172,
+ "EndTime": 2172,
+ "X": 420.331726,
+ "Y": 193.0287
+ },
+ {
+ "StartTime": 2199,
+ "EndTime": 2199,
+ "X": 407.153748,
+ "Y": 193.072342
+ },
+ {
+ "StartTime": 2281,
+ "EndTime": 2281,
+ "X": 367.1318,
+ "Y": 193.204865
+ },
+ {
+ "StartTime": 2363,
+ "EndTime": 2363,
+ "X": 327.1098,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 2445,
+ "EndTime": 2445,
+ "X": 287.08783,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 2527,
+ "EndTime": 2527,
+ "X": 247.065887,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 2609,
+ "EndTime": 2609,
+ "X": 207.043976,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 2691,
+ "EndTime": 2691,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 2773,
+ "EndTime": 2773,
+ "X": 127,
+ "Y": 194
+ },
+ {
+ "StartTime": 2855,
+ "EndTime": 2855,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 2937,
+ "EndTime": 2937,
+ "X": 207.043976,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 3019,
+ "EndTime": 3019,
+ "X": 247.065948,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 3101,
+ "EndTime": 3101,
+ "X": 287.087952,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 3183,
+ "EndTime": 3183,
+ "X": 327.109772,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 3265,
+ "EndTime": 3265,
+ "X": 367.131775,
+ "Y": 193.204865
+ },
+ {
+ "StartTime": 3347,
+ "EndTime": 3347,
+ "X": 407.153748,
+ "Y": 193.072342
+ },
+ {
+ "StartTime": 3374,
+ "EndTime": 3374,
+ "X": 420.331726,
+ "Y": 193.0287
+ },
+ {
+ "StartTime": 3401,
+ "EndTime": 3401,
+ "X": 407.153748,
+ "Y": 193.072342
+ },
+ {
+ "StartTime": 3483,
+ "EndTime": 3483,
+ "X": 367.131775,
+ "Y": 193.204865
+ },
+ {
+ "StartTime": 3565,
+ "EndTime": 3565,
+ "X": 327.109772,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 3647,
+ "EndTime": 3647,
+ "X": 287.087952,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 3729,
+ "EndTime": 3729,
+ "X": 247.065948,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 3811,
+ "EndTime": 3811,
+ "X": 207.043976,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 3893,
+ "EndTime": 3893,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 3975,
+ "EndTime": 3975,
+ "X": 127,
+ "Y": 194
+ },
+ {
+ "StartTime": 4057,
+ "EndTime": 4057,
+ "X": 167.021988,
+ "Y": 193.867477
+ },
+ {
+ "StartTime": 4139,
+ "EndTime": 4139,
+ "X": 207.043976,
+ "Y": 193.734955
+ },
+ {
+ "StartTime": 4221,
+ "EndTime": 4221,
+ "X": 247.065948,
+ "Y": 193.602432
+ },
+ {
+ "StartTime": 4303,
+ "EndTime": 4303,
+ "X": 287.087952,
+ "Y": 193.46991
+ },
+ {
+ "StartTime": 4385,
+ "EndTime": 4385,
+ "X": 327.109772,
+ "Y": 193.337387
+ },
+ {
+ "StartTime": 4467,
+ "EndTime": 4467,
+ "X": 367.131775,
+ "Y": 193.204865
+ },
+ {
+ "StartTime": 4540,
+ "EndTime": 4540,
+ "X": 420.331726,
+ "Y": 193.0287
+ },
+ {
+ "StartTime": 4549,
+ "EndTime": 4549,
+ "X": 407.153748,
+ "Y": 193.072342
+ }
+ ]
+ }]
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu
new file mode 100644
index 0000000000..64aeeb0c07
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu
@@ -0,0 +1,19 @@
+osu file format v14
+
+[General]
+StackLeniency: 0.4
+Mode: 0
+
+[Difficulty]
+CircleSize:4
+OverallDifficulty:7
+ApproachRate:8
+SliderMultiplier:1.6
+SliderTickRate:4
+
+[TimingPoints]
+369,327.868852459016,4,2,2,32,1,0
+
+[HitObjects]
+// A slider with an un-even amount of ticks
+127,194,369,6,0,L|429:193,7,293.333333333333
diff --git a/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
new file mode 100644
index 0000000000..9fba0f1668
--- /dev/null
+++ b/osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
@@ -0,0 +1,116 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Rulesets.Objects;
+
+namespace osu.Game.Tests.Beatmaps
+{
+ [TestFixture]
+ public class SliderEventGenerationTest
+ {
+ private const double start_time = 0;
+ private const double span_duration = 1000;
+
+ [Test]
+ public void TestSingleSpan()
+ {
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray();
+
+ Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
+ Assert.That(events[0].Time, Is.EqualTo(start_time));
+
+ Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[1].Time, Is.EqualTo(span_duration / 2));
+
+ Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tail));
+ Assert.That(events[3].Time, Is.EqualTo(span_duration));
+ }
+
+ [Test]
+ public void TestRepeat()
+ {
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray();
+
+ Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
+ Assert.That(events[0].Time, Is.EqualTo(start_time));
+
+ Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[1].Time, Is.EqualTo(span_duration / 2));
+
+ Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Repeat));
+ Assert.That(events[2].Time, Is.EqualTo(span_duration));
+
+ Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[3].Time, Is.EqualTo(span_duration + span_duration / 2));
+
+ Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tail));
+ Assert.That(events[5].Time, Is.EqualTo(2 * span_duration));
+ }
+
+ [Test]
+ public void TestNonEvenTicks()
+ {
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray();
+
+ Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
+ Assert.That(events[0].Time, Is.EqualTo(start_time));
+
+ Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[1].Time, Is.EqualTo(300));
+
+ Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[2].Time, Is.EqualTo(600));
+
+ Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[3].Time, Is.EqualTo(900));
+
+ Assert.That(events[4].Type, Is.EqualTo(SliderEventType.Repeat));
+ Assert.That(events[4].Time, Is.EqualTo(span_duration));
+
+ Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[5].Time, Is.EqualTo(1100));
+
+ Assert.That(events[6].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[6].Time, Is.EqualTo(1400));
+
+ Assert.That(events[7].Type, Is.EqualTo(SliderEventType.Tick));
+ Assert.That(events[7].Time, Is.EqualTo(1700));
+
+ Assert.That(events[9].Type, Is.EqualTo(SliderEventType.Tail));
+ Assert.That(events[9].Time, Is.EqualTo(2 * span_duration));
+ }
+
+ [Test]
+ public void TestLegacyLastTickOffset()
+ {
+ var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray();
+
+ Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick));
+ Assert.That(events[2].Time, Is.EqualTo(900));
+ }
+
+ [Test]
+ public void TestMinimumTickDistance()
+ {
+ const double velocity = 5;
+ const double min_distance = velocity * 10;
+
+ var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray();
+
+ Assert.Multiple(() =>
+ {
+ int tickIndex = -1;
+
+ while (++tickIndex < events.Length)
+ {
+ if (events[tickIndex].Type != SliderEventType.Tick)
+ continue;
+
+ Assert.That(events[tickIndex].Time, Is.LessThan(span_duration - min_distance).Or.GreaterThan(span_duration + min_distance));
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
index c344cb9598..1f5ba67e03 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs
@@ -9,6 +9,8 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
+using osu.Game.Users;
+using osu.Framework.Bindables;
namespace osu.Game.Tests.Visual.Online
{
@@ -23,18 +25,25 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneProfileRulesetSelector()
{
ProfileRulesetSelector selector;
+ Bindable user = new Bindable();
Child = selector = new ProfileRulesetSelector
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ User = { BindTarget = user }
};
AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo));
AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo));
AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo));
AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo));
- AddStep("select default ruleset", selector.SelectDefaultRuleset);
+
+ AddStep("User with osu as default", () => user.Value = new User { PlayMode = "osu" });
+ AddStep("User with mania as default", () => user.Value = new User { PlayMode = "mania" });
+ AddStep("User with taiko as default", () => user.Value = new User { PlayMode = "taiko" });
+ AddStep("User with catch as default", () => user.Value = new User { PlayMode = "fruits" });
+ AddStep("null user", () => user.Value = null);
}
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 6b3a21a2c1..8605caa5fe 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -150,7 +150,7 @@ namespace osu.Game.Beatmaps
processor?.PostProcess();
foreach (var mod in mods.OfType())
- mod.ApplyToBeatmap(Beatmap);
+ mod.ApplyToBeatmap(converted);
return converted;
}
diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs
index 5ad5e5569a..524a4742c0 100644
--- a/osu.Game/Graphics/ScreenshotManager.cs
+++ b/osu.Game/Graphics/ScreenshotManager.cs
@@ -92,7 +92,8 @@ namespace osu.Game.Graphics
using (var image = await host.TakeScreenshotAsync())
{
- Interlocked.Decrement(ref screenShotTasks);
+ if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false)
+ cursorVisibility.Value = true;
var fileName = getFileName();
if (fileName == null) return;
@@ -125,14 +126,6 @@ namespace osu.Game.Graphics
}
});
- protected override void Update()
- {
- base.Update();
-
- if (cursorVisibility.Value == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
- cursorVisibility.Value = true;
- }
-
private string getFileName()
{
var dt = DateTime.Now;
diff --git a/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs
new file mode 100644
index 0000000000..b7d2222f33
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs
@@ -0,0 +1,44 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osuTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Extensions.Color4Extensions;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ public class DimmedLoadingLayer : VisibilityContainer
+ {
+ private const float transition_duration = 250;
+
+ private readonly LoadingAnimation loading;
+
+ public DimmedLoadingLayer()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
+ },
+ loading = new LoadingAnimation(),
+ };
+ }
+
+ protected override void PopIn()
+ {
+ this.FadeIn(transition_duration, Easing.OutQuint);
+ loading.Show();
+ }
+
+ protected override void PopOut()
+ {
+ this.FadeOut(transition_duration, Easing.OutQuint);
+ loading.Hide();
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
index d158186899..544acc7eb2 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
@@ -81,7 +81,8 @@ namespace osu.Game.Graphics.UserInterface
Colour = Color4.White,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
- }
+ },
+ new HoverClickSounds()
};
Current.ValueChanged += selected =>
diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
index f3384163b8..fb1385793f 100644
--- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Online.API.Requests
{
Favourite,
RankedAndApproved,
+ Loved,
Unranked,
Graveyard
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index e71dd67bf2..b9e2b79b05 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -307,7 +307,9 @@ namespace osu.Game
if (nextBeatmap?.Track != null)
nextBeatmap.Track.Completed += currentTrackCompleted;
- beatmap.OldValue?.Dispose();
+ using (var oldBeatmap = beatmap.OldValue)
+ if (oldBeatmap?.Track != null)
+ oldBeatmap.Track.Completed -= currentTrackCompleted;
nextBeatmap?.LoadBeatmapAsync();
}
diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs
index b6112a6501..2c9a3dd5f9 100644
--- a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs
@@ -2,11 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Rulesets;
+using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
@@ -16,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
private Color4 accentColour = Color4.White;
+ public readonly Bindable User = new Bindable();
+
public ProfileRulesetSelector()
{
TabContainer.Masking = false;
@@ -32,24 +36,17 @@ namespace osu.Game.Overlays.Profile.Header.Components
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
}
- public void SetDefaultRuleset(RulesetInfo ruleset)
+ protected override void LoadComplete()
{
- // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
- foreach (TabItem tabItem in TabContainer)
- ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
+ base.LoadComplete();
+
+ User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true);
}
- public void SelectDefaultRuleset()
+ public void SetDefaultRuleset(RulesetInfo ruleset)
{
- // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
foreach (TabItem tabItem in TabContainer)
- {
- if (((ProfileRulesetTabItem)tabItem).IsDefault)
- {
- Current.Value = ((ProfileRulesetTabItem)tabItem).Value;
- return;
- }
- }
+ ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
}
protected override TabItem CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs
index c5e61f68f4..fa60a37ddb 100644
--- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs
+++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs
@@ -80,7 +80,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
private void load(OsuColour colours)
{
background.Colour = colours.Pink;
- iconContainer.Colour = colours.GreySeafoam;
}
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
index ae91837d29..37f017277f 100644
--- a/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
+++ b/osu.Game/Overlays/Profile/Sections/BeatmapsSection.cs
@@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections
{
new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"),
+ new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, "Loved Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
};
diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs
index a80b7c1108..358f94b659 100644
--- a/osu.Game/Overlays/Settings/Sidebar.cs
+++ b/osu.Game/Overlays/Settings/Sidebar.cs
@@ -84,6 +84,7 @@ namespace osu.Game.Overlays.Settings
Content.Anchor = Anchor.CentreLeft;
Content.Origin = Anchor.CentreLeft;
RelativeSizeAxes = Axes.Both;
+ ScrollbarVisible = false;
}
}
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index 474f529bb1..9dd0def453 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -186,7 +186,7 @@ namespace osu.Game.Overlays
base.UpdateAfterChildren();
ContentContainer.Margin = new MarginPadding { Left = Sidebar?.DrawWidth ?? 0 };
- ContentContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
+ Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
protected class SettingsSectionsContainer : SectionsContainer
diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs
index 7cda7485f9..0d8796b4cb 100644
--- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs
+++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs
@@ -3,13 +3,15 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using osuTK;
namespace osu.Game.Rulesets.Objects
{
public static class SliderEventGenerator
{
- public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, double? legacyLastTickOffset)
+ public static IEnumerable Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount,
+ double? legacyLastTickOffset)
{
// A very lenient maximum length of a slider for ticks to be generated.
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
@@ -36,24 +38,17 @@ namespace osu.Game.Rulesets.Objects
var spanStartTime = startTime + span * spanDuration;
var reversed = span % 2 == 1;
- for (var d = tickDistance; d <= length; d += tickDistance)
+ var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd);
+
+ if (reversed)
{
- if (d >= length - minDistanceFromEnd)
- break;
-
- var pathProgress = d / length;
- var timeProgress = reversed ? 1 - pathProgress : pathProgress;
-
- yield return new SliderEventDescriptor
- {
- Type = SliderEventType.Tick,
- SpanIndex = span,
- SpanStartTime = spanStartTime,
- Time = spanStartTime + timeProgress * spanDuration,
- PathProgress = pathProgress,
- };
+ // For repeat spans, ticks are returned in reverse-StartTime order, which is undesirable for some rulesets
+ ticks = ticks.Reverse();
}
+ foreach (var e in ticks)
+ yield return e;
+
if (span < spanCount - 1)
{
yield return new SliderEventDescriptor
@@ -103,6 +98,40 @@ namespace osu.Game.Rulesets.Objects
PathProgress = spanCount % 2,
};
}
+
+ ///
+ /// Generates the ticks for a span of the slider.
+ ///
+ /// The span index.
+ /// The start time of the span.
+ /// The duration of the span.
+ /// Whether the span is reversed.
+ /// The length of the path.
+ /// The distance between each tick.
+ /// The distance from the end of the path at which ticks are not allowed to be added.
+ /// A for each tick. If is true, the ticks will be returned in reverse-StartTime order.
+ private static IEnumerable generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance,
+ double minDistanceFromEnd)
+ {
+ for (var d = tickDistance; d <= length; d += tickDistance)
+ {
+ if (d >= length - minDistanceFromEnd)
+ break;
+
+ // Always generate ticks from the start of the path rather than the span to ensure that ticks in repeat spans are positioned identically to those in non-repeat spans
+ var pathProgress = d / length;
+ var timeProgress = reversed ? 1 - pathProgress : pathProgress;
+
+ yield return new SliderEventDescriptor
+ {
+ Type = SliderEventType.Tick,
+ SpanIndex = spanIndex,
+ SpanStartTime = spanStartTime,
+ Time = spanStartTime + timeProgress * spanDuration,
+ PathProgress = pathProgress,
+ };
+ }
+ }
}
///
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index 23dd87d8ea..577d999388 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Screens.Select
private readonly MetadataSection description, source, tags;
private readonly Container failRetryContainer;
private readonly FailRetryGraph failRetryGraph;
- private readonly DimmedLoadingAnimation loading;
+ private readonly DimmedLoadingLayer loading;
private IAPIProvider api;
@@ -156,10 +156,7 @@ namespace osu.Game.Screens.Select
},
},
},
- loading = new DimmedLoadingAnimation
- {
- RelativeSizeAxes = Axes.Both,
- },
+ loading = new DimmedLoadingLayer(),
};
}
@@ -365,35 +362,5 @@ namespace osu.Game.Screens.Select
});
}
}
-
- private class DimmedLoadingAnimation : VisibilityContainer
- {
- private readonly LoadingAnimation loading;
-
- public DimmedLoadingAnimation()
- {
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black.Opacity(0.5f),
- },
- loading = new LoadingAnimation(),
- };
- }
-
- protected override void PopIn()
- {
- this.FadeIn(transition_duration, Easing.OutQuint);
- loading.Show();
- }
-
- protected override void PopOut()
- {
- this.FadeOut(transition_duration, Easing.OutQuint);
- loading.Hide();
- }
- }
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1f91ce1cd8..cf325b77be 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist
index 4fbc67e27b..0775d1522d 100644
--- a/osu.iOS/Info.plist
+++ b/osu.iOS/Info.plist
@@ -14,8 +14,6 @@
0.1.0
LSRequiresIPhoneOS
- LSSupportsOpeningDocumentsInPlace
-
MinimumOSVersion
10.0
UIDeviceFamily