diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs
index dad9bbbd0b..78234a9dd9 100644
--- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs
+++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs
@@ -34,7 +34,8 @@ namespace osu.Game.Beatmaps
float ApproachRate { get; }
///
- /// The slider multiplier of the associated beatmap.
+ /// The base slider velocity of the associated beatmap.
+ /// This was known as "SliderMultiplier" in the .osu format and stable editor.
///
double SliderMultiplier { get; }
diff --git a/osu.Game/Localisation/EditorSetupStrings.cs b/osu.Game/Localisation/EditorSetupStrings.cs
index 4ddacf2c5b..caea3dd130 100644
--- a/osu.Game/Localisation/EditorSetupStrings.cs
+++ b/osu.Game/Localisation/EditorSetupStrings.cs
@@ -126,6 +126,16 @@ namespace osu.Game.Localisation
public static LocalisableString OverallDifficultyDescription =>
new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)");
+ ///
+ /// "Base Velocity"
+ ///
+ public static LocalisableString BaseVelocity => new TranslatableString(getKey(@"base_velocity"), @"Base Velocity");
+
+ ///
+ /// "The base velocity of the beatmap, affecting things like slider velocity and scroll speed in some rulesets."
+ ///
+ public static LocalisableString BaseVelocityDescription => new TranslatableString(getKey(@"base_velocity_description"), @"The base velocity of the beatmap, affecting things like slider velocity and scroll speed in some rulesets.");
+
///
/// "Metadata"
///
diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs
index 597925e3e2..7beaf7d086 100644
--- a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (selected is IHasSliderVelocity sliderVelocity)
{
AddHeader("Slider Velocity");
- AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x");
+ AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x ({sliderVelocity.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier:#,0.00}x)");
}
if (selected is IHasRepeats repeats)
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
index 13a1c30cfe..173a665d5c 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs
@@ -96,7 +96,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
RelativeSizeAxes = Axes.X,
Text = "Hold shift while dragging the end of an object to adjust velocity while snapping."
},
- new SliderVelocityInspector(),
+ new SliderVelocityInspector(sliderVelocitySlider.Current),
}
}
};
@@ -145,34 +145,48 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
internal partial class SliderVelocityInspector : EditorInspector
{
+ private readonly Bindable current;
+
+ public SliderVelocityInspector(Bindable current)
+ {
+ this.current = current;
+ }
+
[BackgroundDependencyLoader]
private void load()
{
EditorBeatmap.TransactionBegan += updateInspectorText;
EditorBeatmap.TransactionEnded += updateInspectorText;
+ EditorBeatmap.BeatmapReprocessed += updateInspectorText;
+ current.ValueChanged += _ => updateInspectorText();
+
updateInspectorText();
}
private void updateInspectorText()
{
+ double beatmapVelocity = EditorBeatmap.Difficulty.SliderMultiplier;
+
InspectorText.Clear();
double[] sliderVelocities = EditorBeatmap.HitObjects.OfType().Select(sv => sv.SliderVelocity).OrderBy(v => v).ToArray();
- if (sliderVelocities.Length < 2)
- return;
+ AddHeader("Base velocity (from beatmap setup)");
+ AddValue($"{beatmapVelocity:#,0.00}x");
- double? modeSliderVelocity = sliderVelocities.GroupBy(v => v).MaxBy(v => v.Count())?.Key;
- double? medianSliderVelocity = sliderVelocities[sliderVelocities.Length / 2];
+ AddHeader("Final velocity");
+ AddValue($"{beatmapVelocity * current.Value:#,0.00}x");
- AddHeader("Average velocity");
- AddValue($"{medianSliderVelocity:#,0.00}x");
+ if (sliderVelocities.First() != sliderVelocities.Last())
+ {
+ AddHeader("Beatmap velocity range");
- AddHeader("Most used velocity");
- AddValue($"{modeSliderVelocity:#,0.00}x");
+ string range = $"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x";
+ if (beatmapVelocity != 1)
+ range += $" ({beatmapVelocity * sliderVelocities.First():#,0.00}x - {beatmapVelocity * sliderVelocities.Last():#,0.00}x)";
- AddHeader("Velocity range");
- AddValue($"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x");
+ AddValue(range);
+ }
}
protected override void Dispose(bool isDisposing)
@@ -181,6 +195,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
EditorBeatmap.TransactionBegan -= updateInspectorText;
EditorBeatmap.TransactionEnded -= updateInspectorText;
+ EditorBeatmap.BeatmapReprocessed -= updateInspectorText;
}
}
}
diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs
index 7026bde681..3a3fe7f747 100644
--- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs
+++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs
@@ -19,6 +19,7 @@ namespace osu.Game.Screens.Edit.Setup
private LabelledSliderBar healthDrainSlider = null!;
private LabelledSliderBar approachRateSlider = null!;
private LabelledSliderBar overallDifficultySlider = null!;
+ private LabelledSliderBar baseVelocitySlider = null!;
public override LocalisableString Title => EditorSetupStrings.DifficultyHeader;
@@ -79,13 +80,29 @@ namespace osu.Game.Screens.Edit.Setup
Precision = 0.1f,
}
},
+ baseVelocitySlider = new LabelledSliderBar
+ {
+ Label = EditorSetupStrings.BaseVelocity,
+ FixedLabelWidth = LABEL_WIDTH,
+ Description = EditorSetupStrings.BaseVelocityDescription,
+ Current = new BindableDouble(Beatmap.Difficulty.SliderMultiplier)
+ {
+ Default = 1,
+ MinValue = 0.4,
+ MaxValue = 3.6,
+ Precision = 0.01f,
+ }
+ },
};
foreach (var item in Children.OfType>())
- item.Current.ValueChanged += onValueChanged;
+ item.Current.ValueChanged += _ => updateValues();
+
+ foreach (var item in Children.OfType>())
+ item.Current.ValueChanged += _ => updateValues();
}
- private void onValueChanged(ValueChangedEvent args)
+ private void updateValues()
{
// for now, update these on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
@@ -93,6 +110,7 @@ namespace osu.Game.Screens.Edit.Setup
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value;
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
+ Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
Beatmap.UpdateAllHitObjects();
Beatmap.SaveState();