diff --git a/.github/workflows/report-nunit.yml b/.github/workflows/report-nunit.yml
index e0ccd50989..358cbda17a 100644
--- a/.github/workflows/report-nunit.yml
+++ b/.github/workflows/report-nunit.yml
@@ -30,3 +30,5 @@ jobs:
name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}})
path: "*.trx"
reporter: dotnet-trx
+ list-suites: 'failed'
+ list-tests: 'failed'
diff --git a/osu.Android.props b/osu.Android.props
index db62667fc2..f2052b2563 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 0d6925a83d..6d5a960f06 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -42,9 +42,8 @@ namespace osu.Game.Rulesets.Catch.Objects
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
- DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
- double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
+ double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength;
TickDistance = scoringDistance / difficulty.SliderTickRate;
diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs
index 538a51db5f..5ccb191a9b 100644
--- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs
+++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
@@ -101,27 +102,27 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
throw new System.NotImplementedException();
}
- public override float GetBeatSnapDistanceAt(double referenceTime)
+ public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
throw new System.NotImplementedException();
}
- public override float DurationToDistance(double referenceTime, double duration)
+ public override float DurationToDistance(HitObject referenceObject, double duration)
{
throw new System.NotImplementedException();
}
- public override double DistanceToDuration(double referenceTime, float distance)
+ public override double DistanceToDuration(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
- public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
+ public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
- public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
+ public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
index 471dad87d5..4387bc6b3b 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs
@@ -388,7 +388,7 @@ namespace osu.Game.Rulesets.Mania.Tests
},
};
- beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
+ beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
}
AddStep("load player", () =>
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs
index 18891f8c58..89e13acad6 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs
@@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Tests
},
});
- Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
+ Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 380efff69f..1ed045f7e0 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
Debug.Assert(distanceData != null);
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
- DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
+ DifficultyControlPoint difficultyPoint = hitObject.DifficultyControlPoint;
double beatLength;
#pragma warning disable 618
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
#pragma warning restore 618
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
else
- beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier;
+ beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
SpanCount = repeatsData?.SpanCount() ?? 1;
StartTime = (int)Math.Round(hitObject.StartTime);
diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu
index 7c75b45e5f..ca9e5b0b85 100644
--- a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu
+++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu
@@ -13,6 +13,7 @@ SliderTickRate:1
[TimingPoints]
0,500,4,1,0,100,1,0
+10000,-150,4,1,0,100,1,0
[HitObjects]
51,192,500,128,0,1500:1:0:0:0:
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index 3b7da8d9ba..28e970f397 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mania.UI
// For non-mania beatmap, speed changes should only happen through timing points
if (!isForCurrentRuleset)
- p.DifficultyPoint = new DifficultyControlPoint();
+ p.EffectPoint = new EffectControlPoint();
}
BarLines.ForEach(Playfield.Add);
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs
index 851be2b2f2..ef43c3a696 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs
@@ -11,6 +11,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Objects;
@@ -179,15 +180,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
- public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
+ public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length;
- public float DurationToDistance(double referenceTime, double duration) => (float)duration;
+ public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
- public double DistanceToDuration(double referenceTime, float distance) => distance;
+ public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
- public double GetSnappedDurationFromDistance(double referenceTime, float distance) => 0;
+ public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0;
- public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => 0;
+ public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0;
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
index f09aad8b49..1f01ba601b 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
config.SetValue(OsuSetting.AutoCursorSize, true);
gameplayState.Beatmap.Difficulty.CircleSize = val;
- Scheduler.AddOnce(() => loadContent(false));
+ Scheduler.AddOnce(loadContent);
});
AddStep("test cursor container", () => loadContent(false));
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep($"adjust cs to {circleSize}", () => gameplayState.Beatmap.Difficulty.CircleSize = circleSize);
AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true));
- AddStep("load content", () => loadContent());
+ AddStep("load content", loadContent);
AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale);
@@ -98,7 +98,9 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("load content", () => loadContent(false, () => new SkinProvidingContainer(new TopLeftCursorSkin())));
}
- private void loadContent(bool automated = true, Func skinProvider = null)
+ private void loadContent() => loadContent(false);
+
+ private void loadContent(bool automated, Func skinProvider = null)
{
SetContents(_ =>
{
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
index ececfb0586..d31e7a31f5 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs
@@ -407,8 +407,6 @@ namespace osu.Game.Rulesets.Osu.Tests
},
});
- Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
-
SelectedMods.Value = new[] { new OsuModClassic() };
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
@@ -439,6 +437,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public TestSlider()
{
+ DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
+
DefaultsApplied += _ =>
{
HeadCircle.HitWindows = new TestHitWindows();
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index 81902c25af..03b4254eed 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -13,6 +13,7 @@ using osuTK.Graphics;
using osu.Game.Rulesets.Mods;
using System.Linq;
using NUnit.Framework;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Judgements;
@@ -328,10 +329,14 @@ namespace osu.Game.Rulesets.Osu.Tests
private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier)
{
- var cpi = new ControlPointInfo();
- cpi.Add(0, new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
+ var cpi = new LegacyControlPointInfo();
+ cpi.Add(0, new DifficultyControlPoint { SliderVelocity = speedMultiplier });
- slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 });
+ slider.ApplyDefaults(cpi, new BeatmapDifficulty
+ {
+ CircleSize = circleSize,
+ SliderTickRate = 3
+ });
var drawable = CreateDrawableSlider(slider);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
index 590d159300..f3392724ec 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs
@@ -348,6 +348,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider_start,
Position = new Vector2(0, 0),
+ DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f },
Path = new SliderPath(PathType.PerfectCurve, new[]
{
Vector2.Zero,
@@ -362,8 +363,6 @@ namespace osu.Game.Rulesets.Osu.Tests
},
});
- Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
-
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
index 1b85e0efde..2d43e1b95e 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs
@@ -369,8 +369,6 @@ namespace osu.Game.Rulesets.Osu.Tests
},
});
- Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
-
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
@@ -399,6 +397,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public TestSlider()
{
+ DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
+
DefaultsApplied += _ =>
{
HeadCircle.HitWindows = new TestHitWindows();
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
index a2fc4848af..d82186fb52 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -11,6 +11,7 @@ using System.Linq;
using System.Threading;
using osu.Game.Rulesets.Osu.UI;
using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
@@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
// this results in more (or less) ticks being generated in
+ assertSnapDistance(100 * multiplier, new HitObject
{
- composer.EditorBeatmap.ControlPointInfo.Clear();
- composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = multiplier });
+ DifficultyControlPoint = new DifficultyControlPoint
+ {
+ SliderVelocity = multiplier
+ }
});
-
- assertSnapDistance(100 * multiplier);
}
[TestCase(1)]
@@ -197,20 +196,20 @@ namespace osu.Game.Tests.Editing
assertSnappedDistance(400, 400);
}
- private void assertSnapDistance(float expectedDistance)
- => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(0) == expectedDistance);
+ private void assertSnapDistance(float expectedDistance, HitObject hitObject = null)
+ => AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(hitObject ?? new HitObject()) == expectedDistance);
private void assertDurationToDistance(double duration, float expectedDistance)
- => AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(0, duration) == expectedDistance);
+ => AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(new HitObject(), duration) == expectedDistance);
private void assertDistanceToDuration(float distance, double expectedDuration)
- => AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(0, distance) == expectedDuration);
+ => AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(new HitObject(), distance) == expectedDuration);
private void assertSnappedDuration(float distance, double expectedDuration)
- => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(0, distance) == expectedDuration);
+ => AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(new HitObject(), distance) == expectedDuration);
private void assertSnappedDistance(float distance, float expectedDistance)
- => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(0, distance) == expectedDistance);
+ => AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(new HitObject(), distance) == expectedDistance);
private class TestHitObjectComposer : OsuHitObjectComposer
{
diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
index fabb016d5f..cfda4f6422 100644
--- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
+++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestAddRedundantDifficulty()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
cpi.Add(0, new DifficultyControlPoint()); // is redundant
cpi.Add(1000, new DifficultyControlPoint()); // is redundant
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
- cpi.Add(1000, new DifficultyControlPoint { SpeedMultiplier = 2 }); // is not redundant
+ cpi.Add(1000, new DifficultyControlPoint { SliderVelocity = 2 }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
@@ -159,7 +159,7 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestAddControlPointToGroup()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
@@ -174,23 +174,23 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestAddDuplicateControlPointToGroup()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
group.Add(new DifficultyControlPoint());
- group.Add(new DifficultyControlPoint { SpeedMultiplier = 2 });
+ group.Add(new DifficultyControlPoint { SliderVelocity = 2 });
Assert.That(group.ControlPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
- Assert.That(cpi.DifficultyPoints.First().SpeedMultiplier, Is.EqualTo(2));
+ Assert.That(cpi.DifficultyPoints.First().SliderVelocity, Is.EqualTo(2));
}
[Test]
public void TestRemoveControlPointFromGroup()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
@@ -208,14 +208,14 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestOrdering()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
cpi.Add(0, new TimingControlPoint());
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
- cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 });
- cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 });
+ cpi.Add(3000, new DifficultyControlPoint { SliderVelocity = 2 });
+ cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SliderVelocity = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });
@@ -230,14 +230,14 @@ namespace osu.Game.Tests.NonVisual
[Test]
public void TestClear()
{
- var cpi = new ControlPointInfo();
+ var cpi = new LegacyControlPointInfo();
cpi.Add(0, new TimingControlPoint());
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
- cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 });
- cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 });
+ cpi.Add(3000, new DifficultyControlPoint { SliderVelocity = 2 });
+ cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SliderVelocity = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });
diff --git a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs
index 0107632f6e..99be72e958 100644
--- a/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs
+++ b/osu.Game.Tests/Visual/Audio/TestSceneAudioFilter.cs
@@ -163,5 +163,11 @@ namespace osu.Game.Tests.Visual.Audio
}
private void waitTrackPlay() => AddWaitStep("Let track play", 10);
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ track?.Dispose();
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs
index 11830ebe35..d1efd22d6f 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
@@ -81,7 +82,7 @@ namespace osu.Game.Tests.Visual.Editing
public new float DistanceSpacing => base.DistanceSpacing;
public TestDistanceSnapGrid(double? endTime = null)
- : base(grid_position, 0, endTime)
+ : base(new HitObject(), grid_position, 0, endTime)
{
}
@@ -158,15 +159,15 @@ namespace osu.Game.Tests.Visual.Editing
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
- public float GetBeatSnapDistanceAt(double referenceTime) => 10;
+ public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10;
- public float DurationToDistance(double referenceTime, double duration) => (float)duration;
+ public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
- public double DistanceToDuration(double referenceTime, float distance) => distance;
+ public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
- public double GetSnappedDurationFromDistance(double referenceTime, float distance) => 0;
+ public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0;
- public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => 0;
+ public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0;
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs
index 2f15e549f7..283fe594ea 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScrollingHitObjects.cs
@@ -93,9 +93,9 @@ namespace osu.Game.Tests.Visual.Gameplay
private IList testControlPoints => new List
{
- new MultiplierControlPoint(time_range) { DifficultyPoint = { SpeedMultiplier = 1.25 } },
- new MultiplierControlPoint(1.5 * time_range) { DifficultyPoint = { SpeedMultiplier = 1 } },
- new MultiplierControlPoint(2 * time_range) { DifficultyPoint = { SpeedMultiplier = 1.5 } }
+ new MultiplierControlPoint(time_range) { EffectPoint = { ScrollSpeed = 1.25 } },
+ new MultiplierControlPoint(1.5 * time_range) { EffectPoint = { ScrollSpeed = 1 } },
+ new MultiplierControlPoint(2 * time_range) { EffectPoint = { ScrollSpeed = 1.5 } }
};
[Test]
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index 0b70703870..2bb77395ef 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -564,11 +564,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
});
- AddRepeatStep("click spectate button", () =>
+ AddUntilStep("wait for ready button to be enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value);
+
+ AddStep("click ready button", () =>
{
- InputManager.MoveMouseTo(this.ChildrenOfType().Single());
+ InputManager.MoveMouseTo(readyButton);
InputManager.Click(MouseButton.Left);
- }, 2);
+ });
+
+ AddUntilStep("wait for player to be ready", () => client.Room?.Users[0].State == MultiplayerUserState.Ready);
+ AddUntilStep("wait for ready button to be enabled", () => readyButton.ChildrenOfType().Single().Enabled.Value);
+
+ AddStep("click start button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("wait for player", () => Stack.CurrentScreen is Player);
@@ -582,6 +589,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for results", () => Stack.CurrentScreen is ResultsScreen);
}
+ private MultiplayerReadyButton readyButton => this.ChildrenOfType().Single();
+
private void createRoom(Func room)
{
AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs
index a5b90e6655..0ae4e0c5dc 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs
@@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void assertCollectionDropdownContains(string collectionName, bool shouldContain = true) =>
AddAssert($"collection dropdown {(shouldContain ? "contains" : "does not contain")} '{collectionName}'",
// A bit of a roundabout way of going about this, see: https://github.com/ppy/osu-framework/issues/3871 + https://github.com/ppy/osu-framework/issues/3872
- () => shouldContain == (getCollectionDropdownItems().Any(i => i.ChildrenOfType().OfType().First().Text == collectionName)));
+ () => shouldContain == (getCollectionDropdownItems().Any(i => i.ChildrenOfType().OfType().First().Text == collectionName)));
private IconButton getAddOrRemoveButton(int index)
=> getCollectionDropdownItems().ElementAt(index).ChildrenOfType().Single();
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
index 8203f2e968..4079a0cd5f 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
@@ -15,11 +15,9 @@ namespace osu.Game.Beatmaps.ControlPoints
/// The time at which the control point takes effect.
///
[JsonIgnore]
- public double Time => controlPointGroup?.Time ?? 0;
+ public double Time { get; set; }
- private ControlPointGroup controlPointGroup;
-
- public void AttachGroup(ControlPointGroup pointGroup) => controlPointGroup = pointGroup;
+ public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time;
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
@@ -46,6 +44,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public virtual void CopyFrom(ControlPoint other)
{
+ Time = other.Time;
}
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index 3ff40fe194..9d738ecbfb 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -33,14 +33,6 @@ namespace osu.Game.Beatmaps.ControlPoints
private readonly SortedList timingPoints = new SortedList(Comparer.Default);
- ///
- /// All difficulty points.
- ///
- [JsonProperty]
- public IReadOnlyList DifficultyPoints => difficultyPoints;
-
- private readonly SortedList difficultyPoints = new SortedList(Comparer.Default);
-
///
/// All effect points.
///
@@ -55,14 +47,6 @@ namespace osu.Game.Beatmaps.ControlPoints
[JsonIgnore]
public IEnumerable AllControlPoints => Groups.SelectMany(g => g.ControlPoints).ToArray();
- ///
- /// Finds the difficulty control point that is active at .
- ///
- /// The time to find the difficulty control point at.
- /// The difficulty control point.
- [NotNull]
- public DifficultyControlPoint DifficultyPointAt(double time) => BinarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT);
-
///
/// Finds the effect control point that is active at .
///
@@ -100,7 +84,6 @@ namespace osu.Game.Beatmaps.ControlPoints
{
groups.Clear();
timingPoints.Clear();
- difficultyPoints.Clear();
effectPoints.Clear();
}
@@ -277,10 +260,6 @@ namespace osu.Game.Beatmaps.ControlPoints
case EffectControlPoint _:
existing = EffectPointAt(time);
break;
-
- case DifficultyControlPoint _:
- existing = DifficultyPointAt(time);
- break;
}
return newPoint?.IsRedundant(existing) == true;
@@ -298,9 +277,8 @@ namespace osu.Game.Beatmaps.ControlPoints
effectPoints.Add(typed);
break;
- case DifficultyControlPoint typed:
- difficultyPoints.Add(typed);
- break;
+ default:
+ throw new ArgumentException($"A control point of unexpected type {controlPoint.GetType()} was added to this {nameof(ControlPointInfo)}");
}
}
@@ -315,10 +293,6 @@ namespace osu.Game.Beatmaps.ControlPoints
case EffectControlPoint typed:
effectPoints.Remove(typed);
break;
-
- case DifficultyControlPoint typed:
- difficultyPoints.Remove(typed);
- break;
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
index 8a6cfaf688..bf7ed8e6f5 100644
--- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
@@ -7,17 +7,20 @@ using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints
{
+ ///
+ /// Note that going forward, this control point type should always be assigned directly to HitObjects.
+ ///
public class DifficultyControlPoint : ControlPoint
{
public static readonly DifficultyControlPoint DEFAULT = new DifficultyControlPoint
{
- SpeedMultiplierBindable = { Disabled = true },
+ SliderVelocityBindable = { Disabled = true },
};
///
- /// The speed multiplier at this control point.
+ /// The slider velocity at this control point.
///
- public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1)
+ public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1)
{
Precision = 0.01,
Default = 1,
@@ -28,21 +31,21 @@ namespace osu.Game.Beatmaps.ControlPoints
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1;
///
- /// The speed multiplier at this control point.
+ /// The slider velocity at this control point.
///
- public double SpeedMultiplier
+ public double SliderVelocity
{
- get => SpeedMultiplierBindable.Value;
- set => SpeedMultiplierBindable.Value = value;
+ get => SliderVelocityBindable.Value;
+ set => SliderVelocityBindable.Value = value;
}
public override bool IsRedundant(ControlPoint existing)
=> existing is DifficultyControlPoint existingDifficulty
- && SpeedMultiplier == existingDifficulty.SpeedMultiplier;
+ && SliderVelocity == existingDifficulty.SliderVelocity;
public override void CopyFrom(ControlPoint other)
{
- SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier;
+ SliderVelocity = ((DifficultyControlPoint)other).SliderVelocity;
base.CopyFrom(other);
}
diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
index 79bc88e773..7f550a52fc 100644
--- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
@@ -12,7 +12,8 @@ namespace osu.Game.Beatmaps.ControlPoints
public static readonly EffectControlPoint DEFAULT = new EffectControlPoint
{
KiaiModeBindable = { Disabled = true },
- OmitFirstBarLineBindable = { Disabled = true }
+ OmitFirstBarLineBindable = { Disabled = true },
+ ScrollSpeedBindable = { Disabled = true }
};
///
@@ -20,6 +21,26 @@ namespace osu.Game.Beatmaps.ControlPoints
///
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
+ ///
+ /// The relative scroll speed at this control point.
+ ///
+ public readonly BindableDouble ScrollSpeedBindable = new BindableDouble(1)
+ {
+ Precision = 0.01,
+ Default = 1,
+ MinValue = 0.01,
+ MaxValue = 10
+ };
+
+ ///
+ /// The relative scroll speed.
+ ///
+ public double ScrollSpeed
+ {
+ get => ScrollSpeedBindable.Value;
+ set => ScrollSpeedBindable.Value = value;
+ }
+
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple;
///
@@ -49,12 +70,14 @@ namespace osu.Game.Beatmaps.ControlPoints
=> !OmitFirstBarLine
&& existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode
- && OmitFirstBarLine == existingEffect.OmitFirstBarLine;
+ && OmitFirstBarLine == existingEffect.OmitFirstBarLine
+ && ScrollSpeed == existingEffect.ScrollSpeed;
public override void CopyFrom(ControlPoint other)
{
KiaiMode = ((EffectControlPoint)other).KiaiMode;
OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine;
+ ScrollSpeed = ((EffectControlPoint)other).ScrollSpeed;
base.CopyFrom(other);
}
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index 4aa6a3d6e9..fb489f73b1 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -8,6 +8,9 @@ using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints
{
+ ///
+ /// Note that going forward, this control point type should always be assigned directly to HitObjects.
+ ///
public class SampleControlPoint : ControlPoint
{
public const string DEFAULT_BANK = "normal";
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index f71b148008..bef2d78f21 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -384,14 +384,21 @@ namespace osu.Game.Beatmaps.Formats
addControlPoint(time, new LegacyDifficultyControlPoint(beatLength)
#pragma warning restore 618
{
- SpeedMultiplier = speedMultiplier,
+ SliderVelocity = speedMultiplier,
}, timingChange);
- addControlPoint(time, new EffectControlPoint
+ var effectPoint = new EffectControlPoint
{
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature,
- }, timingChange);
+ };
+
+ bool isOsuRuleset = beatmap.BeatmapInfo.RulesetID == 0;
+ // scrolling rulesets use effect points rather than difficulty points for scroll speed adjustments.
+ if (!isOsuRuleset)
+ effectPoint.ScrollSpeed = speedMultiplier;
+
+ addControlPoint(time, effectPoint, timingChange);
addControlPoint(time, new LegacySampleControlPoint
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 74b3c178cd..1dc270ee63 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -170,33 +170,30 @@ namespace osu.Game.Beatmaps.Formats
if (beatmap.ControlPointInfo.Groups.Count == 0)
return;
+ var legacyControlPoints = new LegacyControlPointInfo();
+ foreach (var point in beatmap.ControlPointInfo.AllControlPoints)
+ legacyControlPoints.Add(point.Time, point.DeepClone());
+
writer.WriteLine("[TimingPoints]");
- if (!(beatmap.ControlPointInfo is LegacyControlPointInfo))
+ SampleControlPoint lastRelevantSamplePoint = null;
+ DifficultyControlPoint lastRelevantDifficultyPoint = null;
+
+ bool isOsuRuleset = beatmap.BeatmapInfo.RulesetID == 0;
+
+ // iterate over hitobjects and pull out all required sample and difficulty changes
+ extractDifficultyControlPoints(beatmap.HitObjects);
+ extractSampleControlPoints(beatmap.HitObjects);
+
+ // handle scroll speed, which is stored as "slider velocity" in legacy formats.
+ // this is relevant for scrolling ruleset beatmaps.
+ if (!isOsuRuleset)
{
- var legacyControlPoints = new LegacyControlPointInfo();
-
- foreach (var point in beatmap.ControlPointInfo.AllControlPoints)
- legacyControlPoints.Add(point.Time, point.DeepClone());
-
- beatmap.ControlPointInfo = legacyControlPoints;
-
- SampleControlPoint lastRelevantSamplePoint = null;
-
- // iterate over hitobjects and pull out all required sample changes
- foreach (var h in beatmap.HitObjects)
- {
- var hSamplePoint = h.SampleControlPoint;
-
- if (!hSamplePoint.IsRedundant(lastRelevantSamplePoint))
- {
- legacyControlPoints.Add(hSamplePoint.Time, hSamplePoint);
- lastRelevantSamplePoint = hSamplePoint;
- }
- }
+ foreach (var point in legacyControlPoints.EffectPoints)
+ legacyControlPoints.Add(point.Time, new DifficultyControlPoint { SliderVelocity = point.ScrollSpeed });
}
- foreach (var group in beatmap.ControlPointInfo.Groups)
+ foreach (var group in legacyControlPoints.Groups)
{
var groupTimingPoint = group.ControlPoints.OfType().FirstOrDefault();
@@ -209,16 +206,16 @@ namespace osu.Game.Beatmaps.Formats
}
// Output any remaining effects as secondary non-timing control point.
- var difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(group.Time);
+ var difficultyPoint = legacyControlPoints.DifficultyPointAt(group.Time);
writer.Write(FormattableString.Invariant($"{group.Time},"));
- writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SpeedMultiplier},"));
+ writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SliderVelocity},"));
outputControlPointAt(group.Time, false);
}
void outputControlPointAt(double time, bool isTimingPoint)
{
- var samplePoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).SamplePointAt(time);
- var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
+ var samplePoint = legacyControlPoints.SamplePointAt(time);
+ var effectPoint = legacyControlPoints.EffectPointAt(time);
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo(string.Empty));
@@ -230,7 +227,7 @@ namespace osu.Game.Beatmaps.Formats
if (effectPoint.OmitFirstBarLine)
effectFlags |= LegacyEffectFlags.OmitFirstBarLine;
- writer.Write(FormattableString.Invariant($"{(int)beatmap.ControlPointInfo.TimingPointAt(time).TimeSignature},"));
+ writer.Write(FormattableString.Invariant($"{(int)legacyControlPoints.TimingPointAt(time).TimeSignature},"));
writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},"));
writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},"));
writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},"));
@@ -238,6 +235,55 @@ namespace osu.Game.Beatmaps.Formats
writer.Write(FormattableString.Invariant($"{(int)effectFlags}"));
writer.WriteLine();
}
+
+ IEnumerable collectDifficultyControlPoints(IEnumerable hitObjects)
+ {
+ if (!isOsuRuleset)
+ yield break;
+
+ foreach (var hitObject in hitObjects)
+ {
+ yield return hitObject.DifficultyControlPoint;
+
+ foreach (var nested in collectDifficultyControlPoints(hitObject.NestedHitObjects))
+ yield return nested;
+ }
+ }
+
+ void extractDifficultyControlPoints(IEnumerable hitObjects)
+ {
+ foreach (var hDifficultyPoint in collectDifficultyControlPoints(hitObjects).OrderBy(dp => dp.Time))
+ {
+ if (!hDifficultyPoint.IsRedundant(lastRelevantDifficultyPoint))
+ {
+ legacyControlPoints.Add(hDifficultyPoint.Time, hDifficultyPoint);
+ lastRelevantDifficultyPoint = hDifficultyPoint;
+ }
+ }
+ }
+
+ IEnumerable collectSampleControlPoints(IEnumerable hitObjects)
+ {
+ foreach (var hitObject in hitObjects)
+ {
+ yield return hitObject.SampleControlPoint;
+
+ foreach (var nested in collectSampleControlPoints(hitObject.NestedHitObjects))
+ yield return nested;
+ }
+ }
+
+ void extractSampleControlPoints(IEnumerable hitObject)
+ {
+ foreach (var hSamplePoint in collectSampleControlPoints(hitObject).OrderBy(sp => sp.Time))
+ {
+ if (!hSamplePoint.IsRedundant(lastRelevantSamplePoint))
+ {
+ legacyControlPoints.Add(hSamplePoint.Time, hSamplePoint);
+ lastRelevantSamplePoint = hSamplePoint;
+ }
+ }
+ }
}
private void handleColours(TextWriter writer)
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 20080308f9..cf6c827af5 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -181,7 +181,7 @@ namespace osu.Game.Beatmaps.Formats
public LegacyDifficultyControlPoint()
{
- SpeedMultiplierBindable.Precision = double.Epsilon;
+ SliderVelocityBindable.Precision = double.Epsilon;
}
public override void CopyFrom(ControlPoint other)
diff --git a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs
index ff0ca5ebe1..2b0a2e7a4d 100644
--- a/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs
+++ b/osu.Game/Beatmaps/Legacy/LegacyControlPointInfo.cs
@@ -1,9 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;
-using osu.Framework.Bindables;
+using osu.Framework.Lists;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Beatmaps.Legacy
@@ -14,9 +15,9 @@ namespace osu.Game.Beatmaps.Legacy
/// All sound points.
///
[JsonProperty]
- public IBindableList SamplePoints => samplePoints;
+ public IReadOnlyList SamplePoints => samplePoints;
- private readonly BindableList samplePoints = new BindableList();
+ private readonly SortedList samplePoints = new SortedList(Comparer.Default);
///
/// Finds the sound control point that is active at .
@@ -26,35 +27,76 @@ namespace osu.Game.Beatmaps.Legacy
[NotNull]
public SampleControlPoint SamplePointAt(double time) => BinarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT);
+ ///
+ /// All difficulty points.
+ ///
+ [JsonProperty]
+ public IReadOnlyList DifficultyPoints => difficultyPoints;
+
+ private readonly SortedList difficultyPoints = new SortedList(Comparer.Default);
+
+ ///
+ /// Finds the difficulty control point that is active at .
+ ///
+ /// The time to find the difficulty control point at.
+ /// The difficulty control point.
+ [NotNull]
+ public DifficultyControlPoint DifficultyPointAt(double time) => BinarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT);
+
public override void Clear()
{
base.Clear();
samplePoints.Clear();
+ difficultyPoints.Clear();
}
protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint)
{
- if (newPoint is SampleControlPoint)
+ switch (newPoint)
{
- var existing = BinarySearch(SamplePoints, time);
- return newPoint.IsRedundant(existing);
- }
+ case SampleControlPoint _:
+ // intentionally don't use SamplePointAt (we always need to consider the first sample point).
+ var existing = BinarySearch(SamplePoints, time);
+ return newPoint.IsRedundant(existing);
- return base.CheckAlreadyExisting(time, newPoint);
+ case DifficultyControlPoint _:
+ return newPoint.IsRedundant(DifficultyPointAt(time));
+
+ default:
+ return base.CheckAlreadyExisting(time, newPoint);
+ }
}
protected override void GroupItemAdded(ControlPoint controlPoint)
{
- if (controlPoint is SampleControlPoint typed)
- samplePoints.Add(typed);
+ switch (controlPoint)
+ {
+ case SampleControlPoint typed:
+ samplePoints.Add(typed);
+ return;
- base.GroupItemAdded(controlPoint);
+ case DifficultyControlPoint typed:
+ difficultyPoints.Add(typed);
+ return;
+
+ default:
+ base.GroupItemAdded(controlPoint);
+ break;
+ }
}
protected override void GroupItemRemoved(ControlPoint controlPoint)
{
- if (controlPoint is SampleControlPoint typed)
- samplePoints.Remove(typed);
+ switch (controlPoint)
+ {
+ case SampleControlPoint typed:
+ samplePoints.Remove(typed);
+ break;
+
+ case DifficultyControlPoint typed:
+ difficultyPoints.Remove(typed);
+ break;
+ }
base.GroupItemRemoved(controlPoint);
}
diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index aaad72f65c..017ea6ec32 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -1,11 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
+using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@@ -141,12 +144,12 @@ namespace osu.Game.Graphics.Containers
Child = box = new Box { RelativeSizeAxes = Axes.Both };
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
Colour = defaultColour = colours.Gray8;
hoverColour = colours.GrayF;
- highlightColour = colours.Green;
+ highlightColour = colourProvider?.Highlight1 ?? colours.Green;
}
public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None)
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index fe88e6f78a..5831d9ab1f 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -1,8 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using System.Linq;
-using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -14,13 +15,15 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public class OsuDropdown : Dropdown, IHasAccentColour
{
- private const float corner_radius = 4;
+ private const float corner_radius = 5;
private Color4 accentColour;
@@ -34,11 +37,11 @@ namespace osu.Game.Graphics.UserInterface
}
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
if (accentColour == default)
- accentColour = colours.PinkDarker;
+ accentColour = colourProvider?.Light4 ?? colours.PinkDarker;
updateAccentColour();
}
@@ -59,14 +62,13 @@ namespace osu.Game.Graphics.UserInterface
{
public override bool HandleNonPositionalInput => State == MenuState.Open;
- private Sample sampleOpen;
- private Sample sampleClose;
+ private Sample? sampleOpen;
+ private Sample? sampleClose;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu()
{
CornerRadius = corner_radius;
- BackgroundColour = Color4.Black.Opacity(0.5f);
MaskingContainer.CornerRadius = corner_radius;
Alpha = 0;
@@ -75,9 +77,11 @@ namespace osu.Game.Graphics.UserInterface
ItemsContainer.Padding = new MarginPadding(5);
}
- [BackgroundDependencyLoader]
- private void load(AudioManager audio)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, AudioManager audio)
{
+ BackgroundColour = colourProvider?.Background5 ?? Color4.Black.Opacity(0.5f);
+
sampleOpen = audio.Samples.Get(@"UI/dropdown-open");
sampleClose = audio.Samples.Get(@"UI/dropdown-close");
}
@@ -159,6 +163,8 @@ namespace osu.Game.Graphics.UserInterface
{
BackgroundColourHover = accentColour ?? nonAccentHoverColour;
BackgroundColourSelected = accentColour ?? nonAccentSelectedColour;
+ BackgroundColour = BackgroundColourHover.Opacity(0);
+
UpdateBackgroundColour();
UpdateForegroundColour();
}
@@ -178,8 +184,6 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- BackgroundColour = Color4.Transparent;
-
nonAccentHoverColour = colours.PinkDarker;
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
updateColours();
@@ -187,16 +191,29 @@ namespace osu.Game.Graphics.UserInterface
AddInternal(new HoverSounds());
}
+ protected override void UpdateBackgroundColour()
+ {
+ if (!IsPreSelected && !IsSelected)
+ {
+ Background.FadeOut(600, Easing.OutQuint);
+ return;
+ }
+
+ Background.FadeIn(100, Easing.OutQuint);
+ Background.FadeColour(IsPreSelected ? BackgroundColourHover : BackgroundColourSelected, 100, Easing.OutQuint);
+ }
+
protected override void UpdateForegroundColour()
{
base.UpdateForegroundColour();
- if (Foreground.Children.FirstOrDefault() is Content content) content.Chevron.Alpha = IsHovered ? 1 : 0;
+ if (Foreground.Children.FirstOrDefault() is Content content)
+ content.Hovering = IsHovered;
}
protected override Drawable CreateContent() => new Content();
- protected new class Content : FillFlowContainer, IHasText
+ protected new class Content : CompositeDrawable, IHasText
{
public LocalisableString Text
{
@@ -207,32 +224,64 @@ namespace osu.Game.Graphics.UserInterface
public readonly OsuSpriteText Label;
public readonly SpriteIcon Chevron;
+ private const float chevron_offset = -3;
+
public Content()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- Direction = FillDirection.Horizontal;
- Children = new Drawable[]
+ InternalChildren = new Drawable[]
{
Chevron = new SpriteIcon
{
- AlwaysPresent = true,
Icon = FontAwesome.Solid.ChevronRight,
- Colour = Color4.Black,
- Alpha = 0.5f,
Size = new Vector2(8),
+ Alpha = 0,
+ X = chevron_offset,
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
Label = new OsuSpriteText
{
+ X = 15,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
};
}
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider)
+ {
+ Chevron.Colour = colourProvider?.Background5 ?? Color4.Black;
+ }
+
+ private bool hovering;
+
+ public bool Hovering
+ {
+ get => hovering;
+ set
+ {
+ if (value == hovering)
+ return;
+
+ hovering = value;
+
+ if (hovering)
+ {
+ Chevron.FadeIn(400, Easing.OutQuint);
+ Chevron.MoveToX(0, 400, Easing.OutQuint);
+ }
+ else
+ {
+ Chevron.FadeOut(200);
+ Chevron.MoveToX(chevron_offset, 200, Easing.In);
+ }
+ }
+ }
}
}
@@ -267,7 +316,7 @@ namespace osu.Game.Graphics.UserInterface
public OsuDropdownHeader()
{
- Foreground.Padding = new MarginPadding(4);
+ Foreground.Padding = new MarginPadding(10);
AutoSizeAxes = Axes.None;
Margin = new MarginPadding { Bottom = 4 };
@@ -303,8 +352,7 @@ namespace osu.Game.Graphics.UserInterface
Icon = FontAwesome.Solid.ChevronDown,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
- Margin = new MarginPadding { Horizontal = 5 },
- Size = new Vector2(12),
+ Size = new Vector2(16),
},
}
}
@@ -313,11 +361,11 @@ namespace osu.Game.Graphics.UserInterface
AddInternal(new HoverClickSounds());
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
- BackgroundColour = Color4.Black.Opacity(0.5f);
- BackgroundColourHover = colours.PinkDarker;
+ BackgroundColour = colourProvider?.Background5 ?? Color4.Black.Opacity(0.5f);
+ BackgroundColourHover = colourProvider?.Light4 ?? colours.PinkDarker;
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs b/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
index 965734792c..c01ee1a059 100644
--- a/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/SlimEnumDropdown.cs
@@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
-using osuTK;
-using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
@@ -15,30 +12,13 @@ namespace osu.Game.Graphics.UserInterface
{
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
- protected override DropdownMenu CreateMenu() => new SlimMenu();
-
private class SlimDropdownHeader : OsuDropdownHeader
{
public SlimDropdownHeader()
{
Height = 25;
- Icon.Size = new Vector2(16);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
}
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- BackgroundColour = Color4.Black.Opacity(0.25f);
- }
- }
-
- private class SlimMenu : OsuDropdownMenu
- {
- public SlimMenu()
- {
- BackgroundColour = Color4.Black.Opacity(0.7f);
- }
}
}
}
diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs
index 5a697623c9..d5f76733cf 100644
--- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs
@@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
+using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Graphics.UserInterfaceV2
@@ -44,6 +47,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
///
protected readonly T Component;
+ private readonly Box background;
private readonly GridContainer grid;
private readonly OsuTextFlowContainer labelText;
private readonly OsuTextFlowContainer descriptionText;
@@ -62,10 +66,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
InternalChildren = new Drawable[]
{
- new Box
+ background = new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex("1c2125"),
},
new FillFlowContainer
{
@@ -146,9 +149,10 @@ namespace osu.Game.Graphics.UserInterfaceV2
}
}
- [BackgroundDependencyLoader]
- private void load(OsuColour osuColour)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, OsuColour osuColour)
{
+ background.Colour = colourProvider?.Background4 ?? Color4Extensions.FromHex(@"1c2125");
descriptionText.Colour = osuColour.Yellow;
}
diff --git a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
index a7fd25b554..deb2e6baf6 100644
--- a/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/SwitchButton.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
@@ -10,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
@@ -66,11 +69,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
};
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ [BackgroundDependencyLoader(true)]
+ private void load(OverlayColourProvider? colourProvider, OsuColour colours)
{
- enabledColour = colours.BlueDark;
- disabledColour = colours.Gray3;
+ enabledColour = colourProvider?.Highlight1 ?? colours.BlueDark;
+ disabledColour = colourProvider?.Background3 ?? colours.Gray3;
switchContainer.Colour = enabledColour;
fill.Colour = disabledColour;
diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs
index 3105ecd742..f8cd31f193 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -31,10 +31,12 @@ namespace osu.Game.Overlays.Notifications
set
{
progress = value;
- Scheduler.AddOnce(() => progressBar.Progress = progress);
+ Scheduler.AddOnce(updateProgress, progress);
}
}
+ private void updateProgress(float progress) => progressBar.Progress = progress;
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs
index 1175ddaab8..a281d03ee7 100644
--- a/osu.Game/Overlays/Settings/SettingsDropdown.cs
+++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs
@@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
+using osuTK;
namespace osu.Game.Overlays.Settings
{
@@ -27,6 +28,11 @@ namespace osu.Game.Overlays.Settings
public override IEnumerable FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString()));
+ public SettingsDropdown()
+ {
+ FlowContent.Spacing = new Vector2(0, 10);
+ }
+
protected sealed override Drawable CreateControl() => CreateDropdown();
protected virtual OsuDropdown CreateDropdown() => new DropdownControl();
@@ -35,7 +41,6 @@ namespace osu.Game.Overlays.Settings
{
public DropdownControl()
{
- Margin = new MarginPadding { Top = 5 };
RelativeSizeAxes = Axes.X;
}
diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
index 9987a0c607..199ba14b48 100644
--- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
+++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
@@ -16,7 +16,6 @@ namespace osu.Game.Overlays.Settings
{
public DropdownControl()
{
- Margin = new MarginPadding { Top = 5 };
RelativeSizeAxes = Axes.X;
}
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index b41e0442bc..91cc80e930 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -13,7 +13,6 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
@@ -389,41 +388,42 @@ namespace osu.Game.Rulesets.Edit
return new SnapResult(screenSpacePosition, targetTime, playfield);
}
- public override float GetBeatSnapDistanceAt(double referenceTime)
+ public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
- DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime);
- return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor);
+ return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
}
- public override float DurationToDistance(double referenceTime, double duration)
+ public override float DurationToDistance(HitObject referenceObject, double duration)
{
- double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
- return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime));
+ double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
+ return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject));
}
- public override double DistanceToDuration(double referenceTime, float distance)
+ public override double DistanceToDuration(HitObject referenceObject, float distance)
{
- double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
- return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength;
+ double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
+ return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength;
}
- public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
- => BeatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime;
+ public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
+ => BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime;
- public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
+ public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
- double actualDuration = referenceTime + DistanceToDuration(referenceTime, distance);
+ double startTime = referenceObject.StartTime;
- double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, referenceTime);
+ double actualDuration = startTime + DistanceToDuration(referenceObject, distance);
- double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
+ double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, startTime);
+
+ double beatLength = BeatSnapProvider.GetBeatLengthAtTime(startTime);
// we don't want to exceed the actual duration and snap to a point in the future.
// as we are snapping to beat length via SnapTime (which will round-to-nearest), check for snapping in the forward direction and reverse it.
if (snappedEndTime > actualDuration + 1)
snappedEndTime -= beatLength;
- return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
+ return DurationToDistance(referenceObject, snappedEndTime - startTime);
}
#endregion
@@ -466,15 +466,15 @@ namespace osu.Game.Rulesets.Edit
public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
- public abstract float GetBeatSnapDistanceAt(double referenceTime);
+ public abstract float GetBeatSnapDistanceAt(HitObject referenceObject);
- public abstract float DurationToDistance(double referenceTime, double duration);
+ public abstract float DurationToDistance(HitObject referenceObject, double duration);
- public abstract double DistanceToDuration(double referenceTime, float distance);
+ public abstract double DistanceToDuration(HitObject referenceObject, float distance);
- public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance);
+ public abstract double GetSnappedDurationFromDistance(HitObject referenceObject, float distance);
- public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance);
+ public abstract float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance);
#endregion
}
diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs
index 4664f3808c..743a2f41fc 100644
--- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs
+++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Rulesets.Edit
@@ -27,41 +28,41 @@ namespace osu.Game.Rulesets.Edit
///
/// Retrieves the distance between two points within a timing point that are one beat length apart.
///
- /// The time of the timing point.
+ /// An object to be used as a reference point for this operation.
/// The distance between two points residing in the timing point that are one beat length apart.
- float GetBeatSnapDistanceAt(double referenceTime);
+ float GetBeatSnapDistanceAt(HitObject referenceObject);
///
/// Converts a duration to a distance.
///
- /// The time of the timing point which resides in.
+ /// An object to be used as a reference point for this operation.
/// The duration to convert.
/// A value that represents as a distance in the timing point.
- float DurationToDistance(double referenceTime, double duration);
+ float DurationToDistance(HitObject referenceObject, double duration);
///
/// Converts a distance to a duration.
///
- /// The time of the timing point which resides in.
+ /// An object to be used as a reference point for this operation.
/// The distance to convert.
/// A value that represents as a duration in the timing point.
- double DistanceToDuration(double referenceTime, float distance);
+ double DistanceToDuration(HitObject referenceObject, float distance);
///
/// Converts a distance to a snapped duration.
///
- /// The time of the timing point which resides in.
+ /// An object to be used as a reference point for this operation.
/// The distance to convert.
/// A value that represents as a duration snapped to the closest beat of the timing point.
- double GetSnappedDurationFromDistance(double referenceTime, float distance);
+ double GetSnappedDurationFromDistance(HitObject referenceObject, float distance);
///
/// Converts an unsnapped distance to a snapped distance.
/// The returned distance will always be floored (as to never exceed the provided .
///
- /// The time of the timing point which resides in.
+ /// An object to be used as a reference point for this operation.
/// The distance to convert.
/// A value that represents snapped to the closest beat of the timing point.
- float GetSnappedDistanceFromDistance(double referenceTime, float distance);
+ float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance);
}
}
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 0b159819d4..035ebe10cb 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -67,7 +67,8 @@ namespace osu.Game.Rulesets.Objects
}
}
- public SampleControlPoint SampleControlPoint;
+ public SampleControlPoint SampleControlPoint = SampleControlPoint.DEFAULT;
+ public DifficultyControlPoint DifficultyControlPoint = DifficultyControlPoint.DEFAULT;
///
/// Whether this is in Kiai time.
@@ -94,6 +95,12 @@ namespace osu.Game.Rulesets.Objects
foreach (var nested in nestedHitObjects)
nested.StartTime += offset;
+
+ if (DifficultyControlPoint != DifficultyControlPoint.DEFAULT)
+ DifficultyControlPoint.Time = time.NewValue;
+
+ if (SampleControlPoint != SampleControlPoint.DEFAULT)
+ SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
};
}
@@ -105,16 +112,21 @@ namespace osu.Game.Rulesets.Objects
/// The cancellation token.
public void ApplyDefaults(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty, CancellationToken cancellationToken = default)
{
+ var legacyInfo = controlPointInfo as LegacyControlPointInfo;
+
+ if (legacyInfo != null)
+ {
+ DifficultyControlPoint = (DifficultyControlPoint)legacyInfo.DifficultyPointAt(StartTime).DeepClone();
+ DifficultyControlPoint.Time = StartTime;
+ }
+
ApplyDefaultsToSelf(controlPointInfo, difficulty);
- if (controlPointInfo is LegacyControlPointInfo legacyInfo)
+ // This is done here after ApplyDefaultsToSelf as we may require custom defaults to be applied to have an accurate end time.
+ if (legacyInfo != null)
{
- // This is done here since ApplyDefaultsToSelf may be used to determine the end time
- SampleControlPoint = legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency);
- }
- else
- {
- SampleControlPoint ??= SampleControlPoint.DEFAULT;
+ SampleControlPoint = (SampleControlPoint)legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency).DeepClone();
+ SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
}
nestedHitObjects.Clear();
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
index e1de82ade7..ad191f7ff5 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs
@@ -43,9 +43,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
- DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
- double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
+ double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength;
}
diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
index dcd2cc8b55..23325bcd13 100644
--- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
+++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
@@ -7,7 +7,7 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Timing
{
///
- /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier.
+ /// A control point which adds an aggregated multiplier based on the provided 's BeatLength and 's SpeedMultiplier.
///
public class MultiplierControlPoint : IComparable
{
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Timing
///
/// The aggregate multiplier which this provides.
///
- public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * BaseBeatLength / TimingPoint.BeatLength;
+ public double Multiplier => Velocity * EffectPoint.ScrollSpeed * BaseBeatLength / TimingPoint.BeatLength;
///
/// The base beat length to scale the provided multiplier relative to.
@@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Timing
public TimingControlPoint TimingPoint = new TimingControlPoint();
///
- /// The that provides additional difficulty information for this .
+ /// The that provides additional difficulty information for this .
///
- public DifficultyControlPoint DifficultyPoint = new DifficultyControlPoint();
+ public EffectControlPoint EffectPoint = new EffectControlPoint();
///
/// Creates a . This is required for JSON serialization
diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
index 041c5ebef5..2a9d3d1cf0 100644
--- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
@@ -140,25 +140,32 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point
var lastTimingPoint = new TimingControlPoint();
- var lastDifficultyPoint = new DifficultyControlPoint();
+ var lastEffectPoint = new EffectControlPoint();
var allPoints = new SortedList(Comparer.Default);
+
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
- allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
+ allPoints.AddRange(Beatmap.ControlPointInfo.EffectPoints);
// Generate the timing points, making non-timing changes use the previous timing change and vice-versa
var timingChanges = allPoints.Select(c =>
{
- if (c is TimingControlPoint timingPoint)
- lastTimingPoint = timingPoint;
- else if (c is DifficultyControlPoint difficultyPoint)
- lastDifficultyPoint = difficultyPoint;
+ switch (c)
+ {
+ case TimingControlPoint timingPoint:
+ lastTimingPoint = timingPoint;
+ break;
+
+ case EffectControlPoint difficultyPoint:
+ lastEffectPoint = difficultyPoint;
+ break;
+ }
return new MultiplierControlPoint(c.Time)
{
Velocity = Beatmap.Difficulty.SliderMultiplier,
BaseBeatLength = baseBeatLength,
TimingPoint = lastTimingPoint,
- DifficultyPoint = lastDifficultyPoint
+ EffectPoint = lastEffectPoint
};
});
diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
index 730f482f83..6b32ff96c4 100644
--- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
@@ -5,14 +5,15 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
+using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public abstract class CircularDistanceSnapGrid : DistanceSnapGrid
{
- protected CircularDistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null)
- : base(startPosition, startTime, endTime)
+ protected CircularDistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
+ : base(referenceObject, startPosition, startTime, endTime)
{
}
@@ -79,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
Vector2 normalisedDirection = direction * new Vector2(1f / distance);
Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius;
- return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(StartTime, (snappedPosition - StartPosition).Length));
+ return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(ReferenceObject, (snappedPosition - StartPosition).Length));
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
index 59f88ac641..9d43e3258a 100644
--- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
@@ -54,15 +55,20 @@ namespace osu.Game.Screens.Edit.Compose.Components
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
private readonly double? endTime;
+ protected readonly HitObject ReferenceObject;
+
///
/// Creates a new .
///
+ /// A reference object to gather relevant difficulty values from.
/// The position at which the grid should start. The first tick is located one distance spacing length away from this point.
/// The snapping time at .
/// The time at which the snapping grid should end. If null, the grid will continue until the bounds of the screen are exceeded.
- protected DistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null)
+ protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
{
+ ReferenceObject = referenceObject;
this.endTime = endTime;
+
StartPosition = startPosition;
StartTime = startTime;
@@ -80,7 +86,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void updateSpacing()
{
- DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(StartTime);
+ DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
if (endTime == null)
MaxIntervals = int.MaxValue;
@@ -88,7 +94,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
// +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors
double maxDuration = endTime.Value - StartTime + 1;
- MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(StartTime, DistanceSpacing));
+ MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceSpacing));
}
gridCache.Invalidate();
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
index 3248936765..cd1ef9127e 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public DifficultyPointPiece(DifficultyControlPoint point)
: base(point)
{
- speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy();
+ speedMultiplier = point.SliderVelocityBindable.GetBoundCopy();
Y = Height;
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 621a24c67d..4296581480 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -15,6 +15,7 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
@@ -298,14 +299,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private double getTimeFromPosition(Vector2 localPosition) =>
(localPosition.X / Content.DrawWidth) * track.Length;
- public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
+ public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException();
- public float DurationToDistance(double referenceTime, double duration) => throw new NotImplementedException();
+ public float DurationToDistance(HitObject referenceObject, double duration) => throw new NotImplementedException();
- public double DistanceToDuration(double referenceTime, float distance) => throw new NotImplementedException();
+ public double DistanceToDuration(HitObject referenceObject, float distance) => throw new NotImplementedException();
- public double GetSnappedDurationFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
+ public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
- public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
+ public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
}
}
diff --git a/osu.Game/Screens/Edit/EditorRoundedScreen.cs b/osu.Game/Screens/Edit/EditorRoundedScreen.cs
index b271a145f5..508663224d 100644
--- a/osu.Game/Screens/Edit/EditorRoundedScreen.cs
+++ b/osu.Game/Screens/Edit/EditorRoundedScreen.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Screens.Edit
{
new Box
{
- Colour = ColourProvider.Dark4,
+ Colour = ColourProvider.Background3,
RelativeSizeAxes = Axes.Both,
},
roundedContent = new Container
diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs
index 97d110c502..52d38f19f6 100644
--- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs
+++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs
@@ -4,21 +4,22 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Screens.Edit.Timing
{
internal class DifficultySection : Section
{
- private SliderWithTextBoxInput multiplierSlider;
+ private SliderWithTextBoxInput sliderVelocitySlider;
[BackgroundDependencyLoader]
private void load()
{
Flow.AddRange(new[]
{
- multiplierSlider = new SliderWithTextBoxInput("Speed Multiplier")
+ sliderVelocitySlider = new SliderWithTextBoxInput("Slider Velocity")
{
- Current = new DifficultyControlPoint().SpeedMultiplierBindable,
+ Current = new DifficultyControlPoint().SliderVelocityBindable,
KeyboardStep = 0.1f
}
});
@@ -28,27 +29,27 @@ namespace osu.Game.Screens.Edit.Timing
{
if (point.NewValue != null)
{
- var selectedPointBindable = point.NewValue.SpeedMultiplierBindable;
+ var selectedPointBindable = point.NewValue.SliderVelocityBindable;
// there may be legacy control points, which contain infinite precision for compatibility reasons (see LegacyDifficultyControlPoint).
// generally that level of precision could only be set by externally editing the .osu file, so at the point
// a user is looking to update this within the editor it should be safe to obliterate this additional precision.
- double expectedPrecision = new DifficultyControlPoint().SpeedMultiplierBindable.Precision;
+ double expectedPrecision = new DifficultyControlPoint().SliderVelocityBindable.Precision;
if (selectedPointBindable.Precision < expectedPrecision)
selectedPointBindable.Precision = expectedPrecision;
- multiplierSlider.Current = selectedPointBindable;
- multiplierSlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
+ sliderVelocitySlider.Current = selectedPointBindable;
+ sliderVelocitySlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
protected override DifficultyControlPoint CreatePoint()
{
- var reference = Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time);
+ var reference = (Beatmap.ControlPointInfo as LegacyControlPointInfo)?.DifficultyPointAt(SelectedGroup.Value.Time) ?? DifficultyControlPoint.DEFAULT;
return new DifficultyControlPoint
{
- SpeedMultiplier = reference.SpeedMultiplier,
+ SliderVelocity = reference.SliderVelocity,
};
}
}
diff --git a/osu.Game/Screens/Edit/Timing/EffectSection.cs b/osu.Game/Screens/Edit/Timing/EffectSection.cs
index 6d23b52c05..c8944d0357 100644
--- a/osu.Game/Screens/Edit/Timing/EffectSection.cs
+++ b/osu.Game/Screens/Edit/Timing/EffectSection.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterfaceV2;
@@ -13,13 +14,20 @@ namespace osu.Game.Screens.Edit.Timing
private LabelledSwitchButton kiai;
private LabelledSwitchButton omitBarLine;
+ private SliderWithTextBoxInput scrollSpeedSlider;
+
[BackgroundDependencyLoader]
private void load()
{
- Flow.AddRange(new[]
+ Flow.AddRange(new Drawable[]
{
kiai = new LabelledSwitchButton { Label = "Kiai Time" },
omitBarLine = new LabelledSwitchButton { Label = "Skip Bar Line" },
+ scrollSpeedSlider = new SliderWithTextBoxInput("Scroll Speed")
+ {
+ Current = new EffectControlPoint().ScrollSpeedBindable,
+ KeyboardStep = 0.1f
+ }
});
}
@@ -32,6 +40,9 @@ namespace osu.Game.Screens.Edit.Timing
omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable;
omitBarLine.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
+
+ scrollSpeedSlider.Current = point.NewValue.ScrollSpeedBindable;
+ scrollSpeedSlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
}
}
@@ -42,7 +53,8 @@ namespace osu.Game.Screens.Edit.Timing
return new EffectControlPoint
{
KiaiMode = reference.KiaiMode,
- OmitFirstBarLine = reference.OmitFirstBarLine
+ OmitFirstBarLine = reference.OmitFirstBarLine,
+ ScrollSpeed = reference.ScrollSpeed,
};
}
}
diff --git a/osu.Game/Screens/Edit/Timing/RowAttributes/DifficultyRowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttributes/DifficultyRowAttribute.cs
index 7b553ac7ad..a8de476d67 100644
--- a/osu.Game/Screens/Edit/Timing/RowAttributes/DifficultyRowAttribute.cs
+++ b/osu.Game/Screens/Edit/Timing/RowAttributes/DifficultyRowAttribute.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
public DifficultyRowAttribute(DifficultyControlPoint difficulty)
: base(difficulty, "difficulty")
{
- speedMultiplier = difficulty.SpeedMultiplierBindable.GetBoundCopy();
+ speedMultiplier = difficulty.SliderVelocityBindable.GetBoundCopy();
}
[BackgroundDependencyLoader]
@@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
},
text = new AttributeText(Point)
{
- Width = 40,
+ Width = 45,
},
});
diff --git a/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs b/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs
index 812407d6da..1b33fd62aa 100644
--- a/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs
+++ b/osu.Game/Screens/Edit/Timing/RowAttributes/EffectRowAttribute.cs
@@ -12,14 +12,18 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
{
private readonly Bindable kiaiMode;
private readonly Bindable omitBarLine;
+ private readonly BindableNumber scrollSpeed;
+
private AttributeText kiaiModeBubble;
private AttributeText omitBarLineBubble;
+ private AttributeText text;
public EffectRowAttribute(EffectControlPoint effect)
: base(effect, "effect")
{
kiaiMode = effect.KiaiModeBindable.GetBoundCopy();
omitBarLine = effect.OmitFirstBarLineBindable.GetBoundCopy();
+ scrollSpeed = effect.ScrollSpeedBindable.GetBoundCopy();
}
[BackgroundDependencyLoader]
@@ -27,12 +31,20 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
{
Content.AddRange(new Drawable[]
{
+ new AttributeProgressBar(Point)
+ {
+ Current = scrollSpeed,
+ },
+ text = new AttributeText(Point) { Width = 45 },
kiaiModeBubble = new AttributeText(Point) { Text = "kiai" },
omitBarLineBubble = new AttributeText(Point) { Text = "no barline" },
});
kiaiMode.BindValueChanged(enabled => kiaiModeBubble.FadeTo(enabled.NewValue ? 1 : 0), true);
omitBarLine.BindValueChanged(enabled => omitBarLineBubble.FadeTo(enabled.NewValue ? 1 : 0), true);
+ scrollSpeed.BindValueChanged(_ => updateText(), true);
}
+
+ private void updateText() => text.Text = $"{scrollSpeed.Value:n2}x";
}
}
diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs
index 52709a2bbe..96a67ab046 100644
--- a/osu.Game/Screens/Edit/Timing/SampleSection.cs
+++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Screens.Edit.Timing
@@ -42,6 +43,15 @@ namespace osu.Game.Screens.Edit.Timing
}
}
- protected override SampleControlPoint CreatePoint() => new SampleControlPoint(); // TODO: remove
+ protected override SampleControlPoint CreatePoint()
+ {
+ var reference = (Beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(SelectedGroup.Value.Time) ?? SampleControlPoint.DEFAULT;
+
+ return new SampleControlPoint
+ {
+ SampleBank = reference.SampleBank,
+ SampleVolume = reference.SampleVolume,
+ };
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index 97377278a6..abda9e897b 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -150,6 +150,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
notifyRoomsUpdated();
}
- private void notifyRoomsUpdated() => Scheduler.AddOnce(() => RoomsUpdated?.Invoke());
+ private void notifyRoomsUpdated()
+ {
+ Scheduler.AddOnce(invokeRoomsUpdated);
+
+ void invokeRoomsUpdated() => RoomsUpdated?.Invoke();
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 80a5daa7c8..0edf5dde6d 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -7,7 +7,6 @@ using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -99,14 +98,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"28242d"),
+ Colour = colourProvider.Background4
},
new GridContainer
{
@@ -249,7 +248,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f),
+ Colour = colourProvider.Background5
},
new FillFlowContainer
{
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index 6c3dfe7382..cf1066df10 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -79,11 +79,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private void load()
{
isConnected.BindTo(client.IsConnected);
- isConnected.BindValueChanged(c => Scheduler.AddOnce(() =>
- {
- if (isConnected.Value && IsLoaded)
- PollImmediately();
- }), true);
+ isConnected.BindValueChanged(c => Scheduler.AddOnce(poll), true);
+ }
+
+ private void poll()
+ {
+ if (isConnected.Value && IsLoaded)
+ PollImmediately();
}
protected override Task Poll()
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
index 0f256160eb..a380ddef25 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomComposite.cs
@@ -19,15 +19,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
base.LoadComplete();
- Client.RoomUpdated += OnRoomUpdated;
-
- Client.UserLeft += UserLeft;
- Client.UserKicked += UserKicked;
- Client.UserJoined += UserJoined;
+ Client.RoomUpdated += invokeOnRoomUpdated;
+ Client.UserLeft += invokeUserLeft;
+ Client.UserKicked += invokeUserKicked;
+ Client.UserJoined += invokeUserJoined;
OnRoomUpdated();
}
+ private void invokeOnRoomUpdated() => Scheduler.AddOnce(OnRoomUpdated);
+ private void invokeUserJoined(MultiplayerRoomUser user) => Scheduler.AddOnce(UserJoined, user);
+ private void invokeUserKicked(MultiplayerRoomUser user) => Scheduler.AddOnce(UserKicked, user);
+ private void invokeUserLeft(MultiplayerRoomUser user) => Scheduler.AddOnce(UserLeft, user);
+
///
/// Invoked when a user has joined the room.
///
@@ -63,10 +67,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
if (Client != null)
{
- Client.UserLeft -= UserLeft;
- Client.UserKicked -= UserKicked;
- Client.UserJoined -= UserJoined;
- Client.RoomUpdated -= OnRoomUpdated;
+ Client.RoomUpdated -= invokeOnRoomUpdated;
+ Client.UserLeft -= invokeUserLeft;
+ Client.UserKicked -= invokeUserKicked;
+ Client.UserJoined -= invokeUserJoined;
}
base.Dispose(isDisposing);
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
index 7384c60888..9e000aa712 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Specialized;
using Humanizer;
using osu.Framework.Allocation;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -77,14 +76,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"28242d"),
+ Colour = colourProvider.Background4
},
new GridContainer
{
@@ -256,7 +255,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f),
+ Colour = colourProvider.Background5
},
new FillFlowContainer
{
diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs
index ea158c5789..e37f42e5f0 100644
--- a/osu.Game/Screens/Play/FailAnimation.cs
+++ b/osu.Game/Screens/Play/FailAnimation.cs
@@ -71,7 +71,6 @@ namespace osu.Game.Screens.Play
this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ =>
{
OnComplete?.Invoke();
- Expire();
});
failLowPassFilter.CutoffTo(300, duration, Easing.OutCubic);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 9ee6f4cf52..af7e9f80c4 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,7 +36,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 110de79285..b3bce3f0ea 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -93,7 +93,7 @@
-
+