mirror of https://github.com/ppy/osu
Merge branch 'master' into fail-animation-update
This commit is contained in:
commit
e9f50179ab
|
@ -8,12 +8,14 @@
|
|||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
|
@ -67,6 +69,9 @@ protected override void LoadComplete()
|
|||
inputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap editorBeatmap { get; set; }
|
||||
|
||||
public override void UpdateTimeAndPosition(SnapResult result)
|
||||
{
|
||||
base.UpdateTimeAndPosition(result);
|
||||
|
@ -75,6 +80,10 @@ public override void UpdateTimeAndPosition(SnapResult result)
|
|||
{
|
||||
case SliderPlacementState.Initial:
|
||||
BeginPlacement();
|
||||
|
||||
var nearestDifficultyPoint = editorBeatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
|
||||
|
||||
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
|
||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,27 +1,106 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class DifficultyPointPiece : TopPointPiece
|
||||
public class DifficultyPointPiece : HitObjectPointPiece, IHasPopover
|
||||
{
|
||||
private readonly HitObject hitObject;
|
||||
|
||||
private readonly BindableNumber<double> speedMultiplier;
|
||||
|
||||
public DifficultyPointPiece(DifficultyControlPoint point)
|
||||
: base(point)
|
||||
public DifficultyPointPiece(HitObject hitObject)
|
||||
: base(hitObject.DifficultyControlPoint)
|
||||
{
|
||||
speedMultiplier = point.SliderVelocityBindable.GetBoundCopy();
|
||||
this.hitObject = hitObject;
|
||||
|
||||
Y = Height;
|
||||
speedMultiplier = hitObject.DifficultyControlPoint.SliderVelocityBindable.GetBoundCopy();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
speedMultiplier.BindValueChanged(multiplier => Label.Text = $"{multiplier.NewValue:n2}x", true);
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
this.ShowPopover();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Popover GetPopover() => new DifficultyEditPopover(hitObject);
|
||||
|
||||
public class DifficultyEditPopover : OsuPopover
|
||||
{
|
||||
private readonly HitObject hitObject;
|
||||
private readonly DifficultyControlPoint point;
|
||||
|
||||
private SliderWithTextBoxInput<double> sliderVelocitySlider;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
|
||||
public DifficultyEditPopover(HitObject hitObject)
|
||||
{
|
||||
this.hitObject = hitObject;
|
||||
point = hitObject.DifficultyControlPoint;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Width = 200,
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
sliderVelocitySlider = new SliderWithTextBoxInput<double>("Velocity")
|
||||
{
|
||||
Current = new DifficultyControlPoint().SliderVelocityBindable,
|
||||
KeyboardStep = 0.1f
|
||||
},
|
||||
new OsuTextFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Hold shift while dragging the end of an object to adjust velocity while snapping."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var selectedPointBindable = point.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().SliderVelocityBindable.Precision;
|
||||
if (selectedPointBindable.Precision < expectedPrecision)
|
||||
selectedPointBindable.Precision = expectedPrecision;
|
||||
|
||||
sliderVelocitySlider.Current = selectedPointBindable;
|
||||
sliderVelocitySlider.Current.BindValueChanged(_ => beatmap?.Update(hitObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class HitObjectPointPiece : CircularContainer
|
||||
{
|
||||
private readonly ControlPoint point;
|
||||
|
||||
protected OsuSpriteText Label { get; private set; }
|
||||
|
||||
protected HitObjectPointPiece(ControlPoint point)
|
||||
{
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Color4 colour = point.GetRepresentingColour(colours);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 16,
|
||||
Masking = true,
|
||||
CornerRadius = 8,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colour,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
Label = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(5),
|
||||
Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold),
|
||||
Colour = colours.B5,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,88 +3,102 @@
|
|||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class SamplePointPiece : CompositeDrawable
|
||||
public class SamplePointPiece : HitObjectPointPiece, IHasPopover
|
||||
{
|
||||
private readonly SampleControlPoint samplePoint;
|
||||
private readonly HitObject hitObject;
|
||||
|
||||
private readonly Bindable<string> bank;
|
||||
private readonly BindableNumber<int> volume;
|
||||
|
||||
private OsuSpriteText text;
|
||||
private Container volumeBox;
|
||||
|
||||
private const int max_volume_height = 22;
|
||||
|
||||
public SamplePointPiece(SampleControlPoint samplePoint)
|
||||
public SamplePointPiece(HitObject hitObject)
|
||||
: base(hitObject.SampleControlPoint)
|
||||
{
|
||||
this.samplePoint = samplePoint;
|
||||
volume = samplePoint.SampleVolumeBindable.GetBoundCopy();
|
||||
bank = samplePoint.SampleBankBindable.GetBoundCopy();
|
||||
this.hitObject = hitObject;
|
||||
volume = hitObject.SampleControlPoint.SampleVolumeBindable.GetBoundCopy();
|
||||
bank = hitObject.SampleControlPoint.SampleBankBindable.GetBoundCopy();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Margin = new MarginPadding { Vertical = 5 };
|
||||
volume.BindValueChanged(volume => updateText());
|
||||
bank.BindValueChanged(bank => updateText(), true);
|
||||
}
|
||||
|
||||
Origin = Anchor.BottomCentre;
|
||||
Anchor = Anchor.BottomCentre;
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
this.ShowPopover();
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
private void updateText()
|
||||
{
|
||||
Label.Text = $"{bank.Value} {volume.Value}";
|
||||
}
|
||||
|
||||
Color4 colour = samplePoint.GetRepresentingColour(colours);
|
||||
public Popover GetPopover() => new SampleEditPopover(hitObject);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
public class SampleEditPopover : OsuPopover
|
||||
{
|
||||
private readonly HitObject hitObject;
|
||||
private readonly SampleControlPoint point;
|
||||
|
||||
private LabelledTextBox bank;
|
||||
private SliderWithTextBoxInput<int> volume;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
|
||||
public SampleEditPopover(HitObject hitObject)
|
||||
{
|
||||
volumeBox = new Circle
|
||||
this.hitObject = hitObject;
|
||||
point = hitObject.SampleControlPoint;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
CornerRadius = 5,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Y = -20,
|
||||
Width = 10,
|
||||
Colour = colour,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 16,
|
||||
Masking = true,
|
||||
CornerRadius = 8,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Children = new Drawable[]
|
||||
new FillFlowContainer
|
||||
{
|
||||
new Box
|
||||
Width = 200,
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Colour = colour,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(5),
|
||||
Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold),
|
||||
Colour = colours.B5,
|
||||
bank = new LabelledTextBox
|
||||
{
|
||||
Label = "Bank Name",
|
||||
},
|
||||
volume = new SliderWithTextBoxInput<int>("Volume")
|
||||
{
|
||||
Current = new SampleControlPoint().SampleVolumeBindable,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
volume.BindValueChanged(volume => volumeBox.Height = max_volume_height * volume.NewValue / 100f, true);
|
||||
bank.BindValueChanged(bank => text.Text = bank.NewValue, true);
|
||||
bank.Current = point.SampleBankBindable;
|
||||
bank.Current.BindValueChanged(_ => beatmap.Update(hitObject));
|
||||
|
||||
volume.Current = point.SampleVolumeBindable;
|
||||
volume.Current.BindValueChanged(_ => beatmap.Update(hitObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class Timeline : ZoomableScrollContainer, IPositionSnapProvider
|
|||
private Track track;
|
||||
|
||||
private const float timeline_height = 72;
|
||||
private const float timeline_expanded_height = 156;
|
||||
private const float timeline_expanded_height = 94;
|
||||
|
||||
public Timeline(Drawable userContent)
|
||||
{
|
||||
|
@ -159,7 +159,7 @@ protected override void LoadComplete()
|
|||
if (visible.NewValue)
|
||||
{
|
||||
this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint);
|
||||
mainContent.MoveToY(36, 200, Easing.OutQuint);
|
||||
mainContent.MoveToY(20, 200, Easing.OutQuint);
|
||||
|
||||
// delay the fade in else masking looks weird.
|
||||
controlPoints.Delay(180).FadeIn(400, Easing.OutQuint);
|
||||
|
|
|
@ -45,17 +45,9 @@ protected override void LoadComplete()
|
|||
{
|
||||
switch (point)
|
||||
{
|
||||
case DifficultyControlPoint difficultyPoint:
|
||||
AddInternal(new DifficultyPointPiece(difficultyPoint) { Depth = -2 });
|
||||
break;
|
||||
|
||||
case TimingControlPoint timingPoint:
|
||||
AddInternal(new TimingPointPiece(timingPoint));
|
||||
break;
|
||||
|
||||
case SampleControlPoint samplePoint:
|
||||
AddInternal(new SamplePointPiece(samplePoint) { Depth = -1 });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
|
@ -179,6 +181,15 @@ private void updateColour()
|
|||
colouredComponents.Colour = OsuColour.ForegroundTextColourFor(col);
|
||||
}
|
||||
|
||||
private SamplePointPiece sampleOverrideDisplay;
|
||||
private DifficultyPointPiece difficultyOverrideDisplay;
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
|
||||
private DifficultyControlPoint difficultyControlPoint;
|
||||
private SampleControlPoint sampleControlPoint;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
@ -194,6 +205,36 @@ protected override void Update()
|
|||
if (Item is IHasRepeats repeats)
|
||||
updateRepeats(repeats);
|
||||
}
|
||||
|
||||
if (difficultyControlPoint != Item.DifficultyControlPoint)
|
||||
{
|
||||
difficultyControlPoint = Item.DifficultyControlPoint;
|
||||
difficultyOverrideDisplay?.Expire();
|
||||
|
||||
if (Item.DifficultyControlPoint != null && Item is IHasDistance)
|
||||
{
|
||||
AddInternal(difficultyOverrideDisplay = new DifficultyPointPiece(Item)
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.BottomCentre
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sampleControlPoint != Item.SampleControlPoint)
|
||||
{
|
||||
sampleControlPoint = Item.SampleControlPoint;
|
||||
sampleOverrideDisplay?.Expire();
|
||||
|
||||
if (Item.SampleControlPoint != null)
|
||||
{
|
||||
AddInternal(sampleOverrideDisplay = new SamplePointPiece(Item)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopCentre
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRepeats(IHasRepeats repeats)
|
||||
|
@ -331,39 +372,66 @@ protected override bool OnDragStart(DragStartEvent e)
|
|||
return true;
|
||||
}
|
||||
|
||||
private ScheduledDelegate dragOperation;
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
base.OnDrag(e);
|
||||
|
||||
OnDragHandled?.Invoke(e);
|
||||
|
||||
if (timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).Time is double time)
|
||||
// schedule is temporary to ensure we don't process multiple times on a single update frame. we need to find a better method of doing this.
|
||||
// without it, a hitobject's endtime may not always be in a valid state (ie. sliders, which needs to recompute their path).
|
||||
dragOperation?.Cancel();
|
||||
dragOperation = Scheduler.Add(() =>
|
||||
{
|
||||
switch (hitObject)
|
||||
OnDragHandled?.Invoke(e);
|
||||
|
||||
if (timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).Time is double time)
|
||||
{
|
||||
case IHasRepeats repeatHitObject:
|
||||
// find the number of repeats which can fit in the requested time.
|
||||
var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1);
|
||||
var proposedCount = Math.Max(0, (int)Math.Round((time - hitObject.StartTime) / lengthOfOneRepeat) - 1);
|
||||
switch (hitObject)
|
||||
{
|
||||
case IHasRepeats repeatHitObject:
|
||||
double proposedDuration = time - hitObject.StartTime;
|
||||
|
||||
if (proposedCount == repeatHitObject.RepeatCount)
|
||||
return;
|
||||
if (e.CurrentState.Keyboard.ShiftPressed)
|
||||
{
|
||||
if (hitObject.DifficultyControlPoint == DifficultyControlPoint.DEFAULT)
|
||||
hitObject.DifficultyControlPoint = new DifficultyControlPoint();
|
||||
|
||||
repeatHitObject.RepeatCount = proposedCount;
|
||||
beatmap.Update(hitObject);
|
||||
break;
|
||||
var newVelocity = hitObject.DifficultyControlPoint.SliderVelocity * (repeatHitObject.Duration / proposedDuration);
|
||||
|
||||
case IHasDuration endTimeHitObject:
|
||||
var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time));
|
||||
if (Precision.AlmostEquals(newVelocity, hitObject.DifficultyControlPoint.SliderVelocity))
|
||||
return;
|
||||
|
||||
if (endTimeHitObject.EndTime == snappedTime || Precision.AlmostEquals(snappedTime, hitObject.StartTime, beatmap.GetBeatLengthAtTime(snappedTime)))
|
||||
return;
|
||||
hitObject.DifficultyControlPoint.SliderVelocity = newVelocity;
|
||||
beatmap.Update(hitObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
// find the number of repeats which can fit in the requested time.
|
||||
var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1);
|
||||
var proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1);
|
||||
|
||||
endTimeHitObject.Duration = snappedTime - hitObject.StartTime;
|
||||
beatmap.Update(hitObject);
|
||||
break;
|
||||
if (proposedCount == repeatHitObject.RepeatCount)
|
||||
return;
|
||||
|
||||
repeatHitObject.RepeatCount = proposedCount;
|
||||
beatmap.Update(hitObject);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IHasDuration endTimeHitObject:
|
||||
var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time));
|
||||
|
||||
if (endTimeHitObject.EndTime == snappedTime || Precision.AlmostEquals(snappedTime, hitObject.StartTime, beatmap.GetBeatLengthAtTime(snappedTime)))
|
||||
return;
|
||||
|
||||
endTimeHitObject.Duration = snappedTime - hitObject.StartTime;
|
||||
beatmap.Update(hitObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
@ -71,6 +72,31 @@ public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, Beatmap
|
|||
{
|
||||
PlayableBeatmap = playableBeatmap;
|
||||
|
||||
// ensure we are not working with legacy control points.
|
||||
// if we leave the legacy points around they will be applied over any local changes on
|
||||
// ApplyDefaults calls. this should eventually be removed once the default logic is moved to the decoder/converter.
|
||||
if (PlayableBeatmap.ControlPointInfo is LegacyControlPointInfo)
|
||||
{
|
||||
var newControlPoints = new ControlPointInfo();
|
||||
|
||||
foreach (var controlPoint in PlayableBeatmap.ControlPointInfo.AllControlPoints)
|
||||
{
|
||||
switch (controlPoint)
|
||||
{
|
||||
case DifficultyControlPoint _:
|
||||
case SampleControlPoint _:
|
||||
// skip legacy types.
|
||||
continue;
|
||||
|
||||
default:
|
||||
newControlPoints.Add(controlPoint.Time, controlPoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
playableBeatmap.ControlPointInfo = newControlPoints;
|
||||
}
|
||||
|
||||
this.beatmapInfo = beatmapInfo ?? playableBeatmap.BeatmapInfo;
|
||||
|
||||
if (beatmapSkin is Skin skin)
|
||||
|
|
|
@ -25,7 +25,9 @@ public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourc
|
|||
|
||||
public double TrackLength => track.Value?.Length ?? 60000;
|
||||
|
||||
public ControlPointInfo ControlPointInfo;
|
||||
public ControlPointInfo ControlPointInfo => Beatmap.ControlPointInfo;
|
||||
|
||||
public IBeatmap Beatmap { get; set; }
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor;
|
||||
|
||||
|
@ -42,25 +44,15 @@ public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourc
|
|||
/// </summary>
|
||||
public bool IsSeeking { get; private set; }
|
||||
|
||||
public EditorClock(IBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
: this(beatmap.ControlPointInfo, beatDivisor)
|
||||
public EditorClock(IBeatmap beatmap = null, BindableBeatDivisor beatDivisor = null)
|
||||
{
|
||||
}
|
||||
Beatmap = beatmap ?? new Beatmap();
|
||||
|
||||
public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor = beatDivisor;
|
||||
|
||||
ControlPointInfo = controlPointInfo;
|
||||
this.beatDivisor = beatDivisor ?? new BindableBeatDivisor();
|
||||
|
||||
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
||||
}
|
||||
|
||||
public EditorClock()
|
||||
: this(new ControlPointInfo(), new BindableBeatDivisor())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to the closest snappable beat from a time.
|
||||
/// </summary>
|
||||
|
|
|
@ -12,8 +12,6 @@ public class ControlPointSettings : EditorRoundedScreenSettings
|
|||
{
|
||||
new GroupSection(),
|
||||
new TimingSection(),
|
||||
new DifficultySection(),
|
||||
new SampleSection(),
|
||||
new EffectSection(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
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<DifficultyControlPoint>
|
||||
{
|
||||
private SliderWithTextBoxInput<double> sliderVelocitySlider;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
sliderVelocitySlider = new SliderWithTextBoxInput<double>("Slider Velocity")
|
||||
{
|
||||
Current = new DifficultyControlPoint().SliderVelocityBindable,
|
||||
KeyboardStep = 0.1f
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnControlPointChanged(ValueChangedEvent<DifficultyControlPoint> point)
|
||||
{
|
||||
if (point.NewValue != null)
|
||||
{
|
||||
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().SliderVelocityBindable.Precision;
|
||||
if (selectedPointBindable.Precision < expectedPrecision)
|
||||
selectedPointBindable.Precision = expectedPrecision;
|
||||
|
||||
sliderVelocitySlider.Current = selectedPointBindable;
|
||||
sliderVelocitySlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
|
||||
}
|
||||
}
|
||||
|
||||
protected override DifficultyControlPoint CreatePoint()
|
||||
{
|
||||
var reference = (Beatmap.ControlPointInfo as LegacyControlPointInfo)?.DifficultyPointAt(SelectedGroup.Value.Time) ?? DifficultyControlPoint.DEFAULT;
|
||||
|
||||
return new DifficultyControlPoint
|
||||
{
|
||||
SliderVelocity = reference.SliderVelocity,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal class SampleSection : Section<SampleControlPoint>
|
||||
{
|
||||
private LabelledTextBox bank;
|
||||
private SliderWithTextBoxInput<int> volume;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new Drawable[]
|
||||
{
|
||||
bank = new LabelledTextBox
|
||||
{
|
||||
Label = "Bank Name",
|
||||
},
|
||||
volume = new SliderWithTextBoxInput<int>("Volume")
|
||||
{
|
||||
Current = new SampleControlPoint().SampleVolumeBindable,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnControlPointChanged(ValueChangedEvent<SampleControlPoint> point)
|
||||
{
|
||||
if (point.NewValue != null)
|
||||
{
|
||||
bank.Current = point.NewValue.SampleBankBindable;
|
||||
bank.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
|
||||
|
||||
volume.Current = point.NewValue.SampleVolumeBindable;
|
||||
volume.Current.BindValueChanged(_ => ChangeHandler?.SaveState());
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Screens.Edit;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
|
@ -23,7 +22,7 @@ public abstract class EditorClockTestScene : OsuManualInputManagerTestScene
|
|||
|
||||
protected EditorClockTestScene()
|
||||
{
|
||||
Clock = new EditorClock(new ControlPointInfo(), BeatDivisor) { IsCoupled = false };
|
||||
Clock = new EditorClock(new Beatmap(), BeatDivisor) { IsCoupled = false };
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
|
@ -44,7 +43,7 @@ private void load()
|
|||
|
||||
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
|
||||
{
|
||||
Clock.ControlPointInfo = e.NewValue.Beatmap.ControlPointInfo;
|
||||
Clock.Beatmap = e.NewValue.Beatmap;
|
||||
Clock.ChangeSource(e.NewValue.Track);
|
||||
Clock.ProcessFrame();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue