Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
Expand Down Expand Up @@ -144,6 +145,75 @@ public void TestMultipleSelectionWithDifferentSliderVelocity()
hitObjectHasVelocity(1, 3);
}

[Test]
public void TestPresetInteractions()
{
clickDifficultyPiece(0);
AddAssert("three presets displayed",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Select(b => b.Velocity),
() => Is.EquivalentTo([0.75d, 1d, 1.5d]));
AddAssert("one preset selected",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Count(b => b.Current.Value == TernaryState.True),
() => Is.EqualTo(1));
AddAssert("selected preset is 1.0x",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Single(b => b.Current.Value == TernaryState.True).Velocity,
() => Is.EqualTo(1));

AddStep("press first preset", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().First());
InputManager.Click(MouseButton.Left);
});
hitObjectHasVelocity(0, 0.75);

dismissPopover();

AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
clickDifficultyPiece(0);
AddAssert("three presets displayed",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Select(b => b.Velocity),
() => Is.EquivalentTo([0.75d, 1d, 1.5d]));
AddAssert("no preset fully selected",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Count(b => b.Current.Value == TernaryState.True),
() => Is.EqualTo(0));
AddAssert("one preset indeterminate",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Count(b => b.Current.Value == TernaryState.Indeterminate),
() => Is.EqualTo(1));

AddStep("remove second preset", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().ElementAt(1));
InputManager.Click(MouseButton.Middle);
});
AddAssert("two presets displayed",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Select(b => b.Velocity),
() => Is.EquivalentTo([0.75d, 1.5d]));
hitObjectHasVelocity(0, 0.75);
hitObjectHasVelocity(1, 2);

AddStep("press last preset", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Last());
InputManager.Click(MouseButton.Left);
});
hitObjectHasVelocity(0, 1.5);
hitObjectHasVelocity(1, 1.5);

setVelocityViaPopover(2);
hitObjectHasVelocity(0, 2);
hitObjectHasVelocity(1, 2);

AddStep("add preset", () =>
{
var popover = this.ChildrenOfType<DifficultyPointPiece.DifficultyEditPopover>().SingleOrDefault();
InputManager.MoveMouseTo(popover.ChildrenOfType<RoundedButton>().First());
InputManager.Click(MouseButton.Left);
});
AddAssert("three presets displayed",
() => this.ChildrenOfType<SliderVelocityAdjustmentControl.SliderVelocityPresetTernaryButton>().Select(b => b.Velocity),
() => Is.EquivalentTo([0.75d, 1.5d, 2d]));
}

private void clickDifficultyPiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () =>
{
var difficultyPiece = this.ChildrenOfType<DifficultyPointPiece>().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex));
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Beatmaps/Beatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ public double GetMostCommonBeatLength()

public int[] Bookmarks { get; set; } = Array.Empty<int>();

public double[] SliderVelocityPresets { get; set; } = [0.75, 1, 1.5];

public int BeatmapVersion { get; set; } = LegacyBeatmapEncoder.FIRST_LAZER_VERSION;

IBeatmap IBeatmap.Clone() => Clone();
Expand Down
1 change: 1 addition & 0 deletions osu.Game/Beatmaps/BeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ protected virtual Beatmap<T> ConvertBeatmap(IBeatmap original, CancellationToken
beatmap.Countdown = original.Countdown;
beatmap.CountdownOffset = original.CountdownOffset;
beatmap.Bookmarks = original.Bookmarks;
beatmap.SliderVelocityPresets = original.SliderVelocityPresets;
beatmap.BeatmapVersion = original.BeatmapVersion;

return beatmap;
Expand Down
8 changes: 8 additions & 0 deletions osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,14 @@ private void handleEditor(string line)
}).Where(p => p.result).Select(p => p.val).ToArray();
break;

case @"VelocityPresets":
beatmap.SliderVelocityPresets = pair.Value.Split(',').Select(v =>
{
bool result = double.TryParse(v, out double val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
break;

case @"DistanceSpacing":
beatmap.DistanceSpacing = Math.Max(0, Parsing.ParseDouble(pair.Value));
break;
Expand Down
1 change: 1 addition & 0 deletions osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private void handleEditor(TextWriter writer)
writer.WriteLine(FormattableString.Invariant($"BeatDivisor: {beatmap.BeatmapInfo.BeatDivisor}"));
writer.WriteLine(FormattableString.Invariant($"GridSize: {beatmap.GridSize}"));
writer.WriteLine(FormattableString.Invariant($"TimelineZoom: {beatmap.TimelineZoom}"));
writer.WriteLine(FormattableString.Invariant($@"VelocityPresets: {string.Join(',', beatmap.SliderVelocityPresets)}"));
}

private void handleMetadata(TextWriter writer)
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Beatmaps/IBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public interface IBeatmap

int[] Bookmarks { get; internal set; }

double[] SliderVelocityPresets { get; internal set; }

int BeatmapVersion { get; }

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,12 @@ public int[] Bookmarks
set => baseBeatmap.Bookmarks = value;
}

public double[] SliderVelocityPresets
{
get => baseBeatmap.SliderVelocityPresets;
set => baseBeatmap.SliderVelocityPresets = value;
}

#endregion
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,37 +98,8 @@ private void load()
// if the piece belongs to an unselected object, operate on that object alone, independently of the selection.
var relevantObjects = (beatmap.SelectedHitObjects.Contains(hitObject) ? beatmap.SelectedHitObjects : hitObject.Yield()).Where(o => o is IHasSliderVelocity).ToArray();

// even if there are multiple objects selected, we can still display a value if they all have the same value.
var selectedPointBindable = relevantObjects.Select(point => ((IHasSliderVelocity)point).SliderVelocityMultiplier).Distinct().Count() == 1
? ((IHasSliderVelocity)relevantObjects.First()).SliderVelocityMultiplierBindable
: null;

if (selectedPointBindable != null)
{
// 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.
adjustmentControl.Current.Value = selectedPointBindable.Value;
}
else
{
adjustmentControl.IsMultipleValues = true;
}

adjustmentControl.Current.BindValueChanged(val =>
{
beatmap.BeginChange();

foreach (var h in relevantObjects)
{
((IHasSliderVelocity)h).SliderVelocityMultiplier = val.NewValue;
beatmap.Update(h);
}

beatmap.EndChange();

adjustmentControl.IsMultipleValues = false;
});
adjustmentControl.ObjectsToAdjust.Clear();
adjustmentControl.ObjectsToAdjust.AddRange(relevantObjects);
}

protected override void LoadComplete()
Expand All @@ -141,9 +112,9 @@ protected override void LoadComplete()

internal partial class SliderVelocityInspector : EditorInspector
{
private readonly Bindable<double> current;
private readonly IBindable<double> current;

public SliderVelocityInspector(Bindable<double> current)
public SliderVelocityInspector(IBindable<double> current)
{
this.current = current;
}
Expand Down
16 changes: 16 additions & 0 deletions osu.Game/Screens/Edit/EditorBeatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, Storybo
EndChange();
});

SliderVelocityPresets = new BindableList<double>(playableBeatmap.SliderVelocityPresets);
SliderVelocityPresets.BindCollectionChanged((_, _) =>
{
BeginChange();
playableBeatmap.SliderVelocityPresets = SliderVelocityPresets.OrderBy(x => x).Distinct().ToArray();
EndChange();
});

PreviewTime = new BindableInt(BeatmapInfo.Metadata.PreviewTime);
PreviewTime.BindValueChanged(s =>
{
Expand Down Expand Up @@ -292,6 +300,14 @@ int[] IBeatmap.Bookmarks
set => PlayableBeatmap.Bookmarks = value;
}

public readonly BindableList<double> SliderVelocityPresets;

double[] IBeatmap.SliderVelocityPresets
{
get => PlayableBeatmap.SliderVelocityPresets;
set => PlayableBeatmap.SliderVelocityPresets = value;
}

public int BeatmapVersion { get; set; }

public IBeatmap Clone() => (EditorBeatmap)MemberwiseClone();
Expand Down
Loading
Loading