From eea87090fb613538e670611bb9e2ad830f23fd83 Mon Sep 17 00:00:00 2001 From: B3nn1 Date: Sat, 6 Jan 2024 19:25:49 +0100 Subject: [PATCH 001/581] Make `changeHandler` save changes to `PathTypes` --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 24e2210b45..0cef93fbb5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -410,8 +410,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ => { + changeHandler?.BeginChange(); foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); + changeHandler?.EndChange(); }); if (countOfState == totalCount) From 26c0d1077a7a4d00fb9ae22bcb16dde08365b987 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 00:22:53 +0100 Subject: [PATCH 002/581] Refactor scale handling in editor to facilitate reuse --- .../Edit/OsuSelectionHandler.cs | 142 +----------- .../Edit/OsuSelectionScaleHandler.cs | 205 ++++++++++++++++++ .../Edit/Compose/Components/SelectionBox.cs | 2 - .../Components/SelectionBoxScaleHandle.cs | 94 +++++++- .../Compose/Components/SelectionHandler.cs | 10 +- .../Components/SelectionScaleHandler.cs | 88 ++++++++ osu.Game/Utils/GeometryUtils.cs | 9 + 7 files changed, 402 insertions(+), 148 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs create mode 100644 osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index cea2adc6e2..c36b535bfa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; @@ -25,15 +24,6 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSelectionHandler : EditorSelectionHandler { - [Resolved(CanBeNull = true)] - private IDistanceSnapProvider? snapProvider { get; set; } - - /// - /// During a transform, the initial path types of a single selected slider are stored so they - /// can be maintained throughout the operation. - /// - private List? referencePathTypes; - protected override void OnSelectionChanged() { base.OnSelectionChanged(); @@ -46,12 +36,6 @@ namespace osu.Game.Rulesets.Osu.Edit SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider); } - protected override void OnOperationEnded() - { - base.OnOperationEnded(); - referencePathTypes = null; - } - protected override bool OnKeyDown(KeyDownEvent e) { if (e.Key == Key.M && e.ControlPressed && e.ShiftPressed) @@ -135,96 +119,9 @@ namespace osu.Game.Rulesets.Osu.Edit return didFlip; } - public override bool HandleScale(Vector2 scale, Anchor reference) - { - adjustScaleFromAnchor(ref scale, reference); - - var hitObjects = selectedMovableObjects; - - // for the time being, allow resizing of slider paths only if the slider is - // the only hit object selected. with a group selection, it's likely the user - // is not looking to change the duration of the slider but expand the whole pattern. - if (hitObjects.Length == 1 && hitObjects.First() is Slider slider) - scaleSlider(slider, scale); - else - scaleHitObjects(hitObjects, reference, scale); - - moveSelectionInBounds(); - return true; - } - - private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) - { - // cancel out scale in axes we don't care about (based on which drag handle was used). - if ((reference & Anchor.x1) > 0) scale.X = 0; - if ((reference & Anchor.y1) > 0) scale.Y = 0; - - // reverse the scale direction if dragging from top or left. - if ((reference & Anchor.x0) > 0) scale.X = -scale.X; - if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; - } - public override SelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(); - private void scaleSlider(Slider slider, Vector2 scale) - { - referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList(); - - Quad sliderQuad = GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position)); - - // Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0. - scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size; - - Vector2 pathRelativeDeltaScale = new Vector2( - sliderQuad.Width == 0 ? 0 : 1 + scale.X / sliderQuad.Width, - sliderQuad.Height == 0 ? 0 : 1 + scale.Y / sliderQuad.Height); - - Queue oldControlPoints = new Queue(); - - foreach (var point in slider.Path.ControlPoints) - { - oldControlPoints.Enqueue(point.Position); - point.Position *= pathRelativeDeltaScale; - } - - // Maintain the path types in case they were defaulted to bezier at some point during scaling - for (int i = 0; i < slider.Path.ControlPoints.Count; ++i) - slider.Path.ControlPoints[i].Type = referencePathTypes[i]; - - // Snap the slider's length to the current beat divisor - // to calculate the final resulting duration / bounding box before the final checks. - slider.SnapTo(snapProvider); - - //if sliderhead or sliderend end up outside playfield, revert scaling. - Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider }); - (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); - - if (xInBounds && yInBounds && slider.Path.HasValidLength) - return; - - foreach (var point in slider.Path.ControlPoints) - point.Position = oldControlPoints.Dequeue(); - - // Snap the slider's length again to undo the potentially-invalid length applied by the previous snap. - slider.SnapTo(snapProvider); - } - - private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) - { - scale = getClampedScale(hitObjects, reference, scale); - Quad selectionQuad = GeometryUtils.GetSurroundingQuad(hitObjects); - - foreach (var h in hitObjects) - h.Position = GeometryUtils.GetScaledPosition(reference, scale, selectionQuad, h.Position); - } - - private (bool X, bool Y) isQuadInBounds(Quad quad) - { - bool xInBounds = (quad.TopLeft.X >= 0) && (quad.BottomRight.X <= DrawWidth); - bool yInBounds = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y <= DrawHeight); - - return (xInBounds, yInBounds); - } + public override SelectionScaleHandler CreateScaleHandler() => new OsuSelectionScaleHandler(); private void moveSelectionInBounds() { @@ -248,43 +145,6 @@ namespace osu.Game.Rulesets.Osu.Edit h.Position += delta; } - /// - /// Clamp scale for multi-object-scaling where selection does not exceed playfield bounds or flip. - /// - /// The hitobjects to be scaled - /// The anchor from which the scale operation is performed - /// The scale to be clamped - /// The clamped scale vector - private Vector2 getClampedScale(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) - { - float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; - float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; - - Quad selectionQuad = GeometryUtils.GetSurroundingQuad(hitObjects); - - //todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead. - Quad scaledQuad = new Quad(selectionQuad.TopLeft.X + xOffset, selectionQuad.TopLeft.Y + yOffset, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); - - //max Size -> playfield bounds - if (scaledQuad.TopLeft.X < 0) - scale.X += scaledQuad.TopLeft.X; - if (scaledQuad.TopLeft.Y < 0) - scale.Y += scaledQuad.TopLeft.Y; - - if (scaledQuad.BottomRight.X > DrawWidth) - scale.X -= scaledQuad.BottomRight.X - DrawWidth; - if (scaledQuad.BottomRight.Y > DrawHeight) - scale.Y -= scaledQuad.BottomRight.Y - DrawHeight; - - //min Size -> almost 0. Less than 0 causes the quad to flip, exactly 0 causes scaling to get stuck at minimum scale. - Vector2 scaledSize = selectionQuad.Size + scale; - Vector2 minSize = new Vector2(Precision.FLOAT_EPSILON); - - scale = Vector2.ComponentMax(minSize, scaledSize) - selectionQuad.Size; - - return scale; - } - /// /// All osu! hitobjects which can be moved/rotated/scaled. /// diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs new file mode 100644 index 0000000000..8068c73131 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -0,0 +1,205 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Utils; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public partial class OsuSelectionScaleHandler : SelectionScaleHandler + { + [Resolved] + private IEditorChangeHandler? changeHandler { get; set; } + + [Resolved(CanBeNull = true)] + private IDistanceSnapProvider? snapProvider { get; set; } + + private BindableList selectedItems { get; } = new BindableList(); + + [BackgroundDependencyLoader] + private void load(EditorBeatmap editorBeatmap) + { + selectedItems.BindTo(editorBeatmap.SelectedHitObjects); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedItems.CollectionChanged += (_, __) => updateState(); + updateState(); + } + + private void updateState() + { + var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects); + CanScale.Value = quad.Width > 0 || quad.Height > 0; + } + + private OsuHitObject[]? objectsInScale; + + private Vector2? defaultOrigin; + private Dictionary? originalPositions; + private Dictionary? originalPathControlPointPositions; + private Dictionary? originalPathControlPointTypes; + + public override void Begin() + { + if (objectsInScale != null) + throw new InvalidOperationException($"Cannot {nameof(Begin)} a scale operation while another is in progress!"); + + changeHandler?.BeginChange(); + + objectsInScale = selectedMovableObjects.ToArray(); + OriginalSurroundingQuad = objectsInScale.Length == 1 && objectsInScale.First() is Slider slider + ? GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position)) + : GeometryUtils.GetSurroundingQuad(objectsInScale); + defaultOrigin = OriginalSurroundingQuad.Value.Centre; + originalPositions = objectsInScale.ToDictionary(obj => obj, obj => obj.Position); + originalPathControlPointPositions = objectsInScale.OfType().ToDictionary( + obj => obj, + obj => obj.Path.ControlPoints.Select(point => point.Position).ToArray()); + originalPathControlPointTypes = objectsInScale.OfType().ToDictionary( + obj => obj, + obj => obj.Path.ControlPoints.Select(p => p.Type).ToArray()); + } + + public override void Update(Vector2 scale, Vector2? origin = null) + { + if (objectsInScale == null) + throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); + + Debug.Assert(originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null && originalPathControlPointTypes != null && OriginalSurroundingQuad != null); + + Vector2 actualOrigin = origin ?? defaultOrigin.Value; + + // for the time being, allow resizing of slider paths only if the slider is + // the only hit object selected. with a group selection, it's likely the user + // is not looking to change the duration of the slider but expand the whole pattern. + if (objectsInScale.Length == 1 && objectsInScale.First() is Slider slider) + scaleSlider(slider, scale, originalPathControlPointPositions[slider], originalPathControlPointTypes[slider]); + else + { + scale = getClampedScale(OriginalSurroundingQuad.Value, actualOrigin, scale); + + foreach (var ho in objectsInScale) + { + ho.Position = GeometryUtils.GetScaledPositionMultiply(scale, actualOrigin, originalPositions[ho]); + } + } + + moveSelectionInBounds(); + } + + public override void Commit() + { + if (objectsInScale == null) + throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!"); + + changeHandler?.EndChange(); + + objectsInScale = null; + OriginalSurroundingQuad = null; + originalPositions = null; + originalPathControlPointPositions = null; + originalPathControlPointTypes = null; + defaultOrigin = null; + } + + private IEnumerable selectedMovableObjects => selectedItems.Cast() + .Where(h => h is not Spinner); + + private void scaleSlider(Slider slider, Vector2 scale, Vector2[] originalPathPositions, PathType?[] originalPathTypes) + { + // Maintain the path types in case they were defaulted to bezier at some point during scaling + for (int i = 0; i < slider.Path.ControlPoints.Count; i++) + { + slider.Path.ControlPoints[i].Position = originalPathPositions[i] * scale; + slider.Path.ControlPoints[i].Type = originalPathTypes[i]; + } + + // Snap the slider's length to the current beat divisor + // to calculate the final resulting duration / bounding box before the final checks. + slider.SnapTo(snapProvider); + + //if sliderhead or sliderend end up outside playfield, revert scaling. + Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider }); + (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); + + if (xInBounds && yInBounds && slider.Path.HasValidLength) + return; + + for (int i = 0; i < slider.Path.ControlPoints.Count; i++) + slider.Path.ControlPoints[i].Position = originalPathPositions[i]; + + // Snap the slider's length again to undo the potentially-invalid length applied by the previous snap. + slider.SnapTo(snapProvider); + } + + private (bool X, bool Y) isQuadInBounds(Quad quad) + { + bool xInBounds = (quad.TopLeft.X >= 0) && (quad.BottomRight.X <= OsuPlayfield.BASE_SIZE.X); + bool yInBounds = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y <= OsuPlayfield.BASE_SIZE.Y); + + return (xInBounds, yInBounds); + } + + /// + /// Clamp scale for multi-object-scaling where selection does not exceed playfield bounds or flip. + /// + /// The quad surrounding the hitobjects + /// The origin from which the scale operation is performed + /// The scale to be clamped + /// The clamped scale vector + private Vector2 getClampedScale(Quad selectionQuad, Vector2 origin, Vector2 scale) + { + //todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead. + + var tl1 = Vector2.Divide(-origin, selectionQuad.TopLeft - origin); + var tl2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - origin, selectionQuad.TopLeft - origin); + var br1 = Vector2.Divide(-origin, selectionQuad.BottomRight - origin); + var br2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - origin, selectionQuad.BottomRight - origin); + + scale.X = selectionQuad.TopLeft.X - origin.X < 0 ? MathHelper.Clamp(scale.X, tl2.X, tl1.X) : MathHelper.Clamp(scale.X, tl1.X, tl2.X); + scale.Y = selectionQuad.TopLeft.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, tl2.Y, tl1.Y) : MathHelper.Clamp(scale.Y, tl1.Y, tl2.Y); + scale.X = selectionQuad.BottomRight.X - origin.X < 0 ? MathHelper.Clamp(scale.X, br2.X, br1.X) : MathHelper.Clamp(scale.X, br1.X, br2.X); + scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y); + + return scale; + } + + private void moveSelectionInBounds() + { + Quad quad = GeometryUtils.GetSurroundingQuad(objectsInScale!); + + Vector2 delta = Vector2.Zero; + + if (quad.TopLeft.X < 0) + delta.X -= quad.TopLeft.X; + if (quad.TopLeft.Y < 0) + delta.Y -= quad.TopLeft.Y; + + if (quad.BottomRight.X > OsuPlayfield.BASE_SIZE.X) + delta.X -= quad.BottomRight.X - OsuPlayfield.BASE_SIZE.X; + if (quad.BottomRight.Y > OsuPlayfield.BASE_SIZE.Y) + delta.Y -= quad.BottomRight.Y - OsuPlayfield.BASE_SIZE.Y; + + foreach (var h in objectsInScale!) + h.Position += delta; + } + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 0b16941bc4..e8b3e430eb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private SelectionRotationHandler? rotationHandler { get; set; } - public Func? OnScale; public Func? OnFlip; public Func? OnReverse; @@ -353,7 +352,6 @@ namespace osu.Game.Screens.Edit.Compose.Components var handle = new SelectionBoxScaleHandle { Anchor = anchor, - HandleScale = (delta, a) => OnScale?.Invoke(delta, a) }; handle.OperationStarted += operationStarted; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 7943065c82..56c5585ae7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -1,19 +1,22 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { public partial class SelectionBoxScaleHandle : SelectionBoxDragHandle { - public Action HandleScale { get; set; } + [Resolved] + private SelectionBox selectionBox { get; set; } = null!; + + [Resolved] + private SelectionScaleHandler? scaleHandler { get; set; } [BackgroundDependencyLoader] private void load() @@ -21,10 +24,93 @@ namespace osu.Game.Screens.Edit.Compose.Components Size = new Vector2(10); } + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button != MouseButton.Left) + return false; + + if (scaleHandler == null) return false; + + scaleHandler.Begin(); + return true; + } + + private Vector2 getOriginPosition() + { + var quad = scaleHandler!.OriginalSurroundingQuad!.Value; + Vector2 origin = quad.TopLeft; + + if ((Anchor & Anchor.x0) > 0) + origin.X += quad.Width; + + if ((Anchor & Anchor.y0) > 0) + origin.Y += quad.Height; + + return origin; + } + + private Vector2 rawScale; + protected override void OnDrag(DragEvent e) { - HandleScale?.Invoke(e.Delta, Anchor); base.OnDrag(e); + + if (scaleHandler == null) return; + + rawScale = convertDragEventToScaleMultiplier(e); + + applyScale(shouldKeepAspectRatio: e.ShiftPressed); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + { + applyScale(shouldKeepAspectRatio: true); + return true; + } + + return base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyUpEvent e) + { + base.OnKeyUp(e); + + if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + applyScale(shouldKeepAspectRatio: false); + } + + protected override void OnDragEnd(DragEndEvent e) + { + scaleHandler?.Commit(); + } + + private Vector2 convertDragEventToScaleMultiplier(DragEvent e) + { + Vector2 scale = e.MousePosition - e.MouseDownPosition; + adjustScaleFromAnchor(ref scale); + return Vector2.Divide(scale, scaleHandler!.OriginalSurroundingQuad!.Value.Size) + Vector2.One; + } + + private void adjustScaleFromAnchor(ref Vector2 scale) + { + // cancel out scale in axes we don't care about (based on which drag handle was used). + if ((Anchor & Anchor.x1) > 0) scale.X = 1; + if ((Anchor & Anchor.y1) > 0) scale.Y = 1; + + // reverse the scale direction if dragging from top or left. + if ((Anchor & Anchor.x0) > 0) scale.X = -scale.X; + if ((Anchor & Anchor.y0) > 0) scale.Y = -scale.Y; + } + + private void applyScale(bool shouldKeepAspectRatio) + { + var newScale = shouldKeepAspectRatio + ? new Vector2(MathF.Max(rawScale.X, rawScale.Y)) + : rawScale; + + scaleHandler!.Update(newScale, getOriginPosition()); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 3c859c65ff..dd6bd43f4d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -57,6 +57,8 @@ namespace osu.Game.Screens.Edit.Compose.Components public SelectionRotationHandler RotationHandler { get; private set; } + public SelectionScaleHandler ScaleHandler { get; private set; } + protected SelectionHandler() { selectedBlueprints = new List>(); @@ -69,6 +71,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(RotationHandler = CreateRotationHandler()); + dependencies.CacheAs(ScaleHandler = CreateScaleHandler()); return dependencies; } @@ -78,6 +81,7 @@ namespace osu.Game.Screens.Edit.Compose.Components AddRangeInternal(new Drawable[] { RotationHandler, + ScaleHandler, SelectionBox = CreateSelectionBox(), }); @@ -93,7 +97,6 @@ namespace osu.Game.Screens.Edit.Compose.Components OperationStarted = OnOperationBegan, OperationEnded = OnOperationEnded, - OnScale = HandleScale, OnFlip = HandleFlip, OnReverse = HandleReverse, }; @@ -157,6 +160,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether any items could be scaled. public virtual bool HandleScale(Vector2 scale, Anchor anchor) => false; + /// + /// Creates the handler to use for scale operations. + /// + public virtual SelectionScaleHandler CreateScaleHandler() => new SelectionScaleHandler(); + /// /// Handles the selected items being flipped. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs new file mode 100644 index 0000000000..b7c8f16a02 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs @@ -0,0 +1,88 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + /// + /// Base handler for editor scale operations. + /// + public partial class SelectionScaleHandler : Component + { + /// + /// Whether the scale can currently be performed. + /// + public Bindable CanScale { get; private set; } = new BindableBool(); + + public Quad? OriginalSurroundingQuad { get; protected set; } + + /// + /// Performs a single, instant, atomic scale operation. + /// + /// + /// This method is intended to be used in atomic contexts (such as when pressing a single button). + /// For continuous operations, see the -- flow. + /// + /// The scale to apply, as multiplier. + /// + /// The origin point to scale from. + /// If the default value is supplied, a sane implementation-defined default will be used. + /// + public void ScaleSelection(Vector2 scale, Vector2? origin = null) + { + Begin(); + Update(scale, origin); + Commit(); + } + + /// + /// Begins a continuous scale operation. + /// + /// + /// This flow is intended to be used when a scale operation is made incrementally (such as when dragging a scale handle or slider). + /// For instantaneous, atomic operations, use the convenience method. + /// + public virtual void Begin() + { + } + + /// + /// Updates a continuous scale operation. + /// Must be preceded by a call. + /// + /// + /// + /// This flow is intended to be used when a scale operation is made incrementally (such as when dragging a scale handle or slider). + /// As such, the values of and supplied should be relative to the state of the objects being scaled + /// when was called, rather than instantaneous deltas. + /// + /// + /// For instantaneous, atomic operations, use the convenience method. + /// + /// + /// The Scale to apply, as multiplier. + /// + /// The origin point to scale from. + /// If the default value is supplied, a sane implementation-defined default will be used. + /// + public virtual void Update(Vector2 scale, Vector2? origin = null) + { + } + + /// + /// Ends a continuous scale operation. + /// Must be preceded by a call. + /// + /// + /// This flow is intended to be used when a scale operation is made incrementally (such as when dragging a scale handle or slider). + /// For instantaneous, atomic operations, use the convenience method. + /// + public virtual void Commit() + { + } + } +} diff --git a/osu.Game/Utils/GeometryUtils.cs b/osu.Game/Utils/GeometryUtils.cs index 725e93d098..ef362d8223 100644 --- a/osu.Game/Utils/GeometryUtils.cs +++ b/osu.Game/Utils/GeometryUtils.cs @@ -79,6 +79,15 @@ namespace osu.Game.Utils return position; } + /// + /// Given a scale multiplier, an origin, and a position, + /// will return the scaled position in screen space coordinates. + /// + public static Vector2 GetScaledPositionMultiply(Vector2 scale, Vector2 origin, Vector2 position) + { + return origin + (position - origin) * scale; + } + /// /// Returns a quad surrounding the provided points. /// From a4f771ec089baff91ddea3d4714355e64a8237dd Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 01:13:01 +0100 Subject: [PATCH 003/581] refactor CanScale properties --- .../Edit/OsuSelectionHandler.cs | 5 +- .../Edit/OsuSelectionScaleHandler.cs | 7 +- .../SkinEditor/SkinSelectionHandler.cs | 3 - .../Edit/Compose/Components/SelectionBox.cs | 76 +++++-------------- .../Components/SelectionScaleHandler.cs | 18 ++++- osu.Game/Utils/GeometryUtils.cs | 2 +- 6 files changed, 44 insertions(+), 67 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index c36b535bfa..00c90cdbd6 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -30,9 +30,8 @@ namespace osu.Game.Rulesets.Osu.Edit Quad quad = selectedMovableObjects.Length > 0 ? GeometryUtils.GetSurroundingQuad(selectedMovableObjects) : new Quad(); - SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0; - SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0; - SelectionBox.CanScaleDiagonally = SelectionBox.CanScaleX && SelectionBox.CanScaleY; + SelectionBox.CanFlipX = quad.Width > 0; + SelectionBox.CanFlipY = quad.Height > 0; SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 8068c73131..7b0ae947e7 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -47,7 +47,10 @@ namespace osu.Game.Rulesets.Osu.Edit private void updateState() { var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects); - CanScale.Value = quad.Width > 0 || quad.Height > 0; + + CanScaleX.Value = quad.Width > 0; + CanScaleY.Value = quad.Height > 0; + CanScaleDiagonally.Value = CanScaleX.Value && CanScaleY.Value; } private OsuHitObject[]? objectsInScale; @@ -98,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var ho in objectsInScale) { - ho.Position = GeometryUtils.GetScaledPositionMultiply(scale, actualOrigin, originalPositions[ho]); + ho.Position = GeometryUtils.GetScaledPosition(scale, actualOrigin, originalPositions[ho]); } } diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index cf6fb60636..efca6f0080 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -218,9 +218,6 @@ namespace osu.Game.Overlays.SkinEditor { base.OnSelectionChanged(); - SelectionBox.CanScaleX = allSelectedSupportManualSizing(Axes.X); - SelectionBox.CanScaleY = allSelectedSupportManualSizing(Axes.Y); - SelectionBox.CanScaleDiagonally = true; SelectionBox.CanFlipX = true; SelectionBox.CanFlipY = true; SelectionBox.CanReverse = false; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index e8b3e430eb..2329a466fe 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -27,6 +27,9 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved] private SelectionRotationHandler? rotationHandler { get; set; } + [Resolved] + private SelectionScaleHandler? scaleHandler { get; set; } + public Func? OnFlip; public Func? OnReverse; @@ -56,60 +59,11 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly IBindable canRotate = new BindableBool(); - private bool canScaleX; + private readonly IBindable canScaleX = new BindableBool(); - /// - /// Whether horizontal scaling (from the left or right edge) support should be enabled. - /// - public bool CanScaleX - { - get => canScaleX; - set - { - if (canScaleX == value) return; + private readonly IBindable canScaleY = new BindableBool(); - canScaleX = value; - recreate(); - } - } - - private bool canScaleY; - - /// - /// Whether vertical scaling (from the top or bottom edge) support should be enabled. - /// - public bool CanScaleY - { - get => canScaleY; - set - { - if (canScaleY == value) return; - - canScaleY = value; - recreate(); - } - } - - private bool canScaleDiagonally; - - /// - /// Whether diagonal scaling (from a corner) support should be enabled. - /// - /// - /// There are some cases where we only want to allow proportional resizing, and not allow - /// one or both explicit directions of scale. - /// - public bool CanScaleDiagonally - { - get => canScaleDiagonally; - set - { - if (canScaleDiagonally == value) return; - - canScaleDiagonally = value; - recreate(); - } - } + private readonly IBindable canScaleDiagonally = new BindableBool(); private bool canFlipX; @@ -175,7 +129,17 @@ namespace osu.Game.Screens.Edit.Compose.Components if (rotationHandler != null) canRotate.BindTo(rotationHandler.CanRotate); - canRotate.BindValueChanged(_ => recreate(), true); + if (scaleHandler != null) + { + canScaleX.BindTo(scaleHandler.CanScaleX); + canScaleY.BindTo(scaleHandler.CanScaleY); + canScaleDiagonally.BindTo(scaleHandler.CanScaleDiagonally); + } + + canRotate.BindValueChanged(_ => recreate()); + canScaleX.BindValueChanged(_ => recreate()); + canScaleY.BindValueChanged(_ => recreate()); + canScaleDiagonally.BindValueChanged(_ => recreate(), true); } protected override bool OnKeyDown(KeyDownEvent e) @@ -264,9 +228,9 @@ namespace osu.Game.Screens.Edit.Compose.Components } }; - if (CanScaleX) addXScaleComponents(); - if (CanScaleDiagonally) addFullScaleComponents(); - if (CanScaleY) addYScaleComponents(); + if (canScaleX.Value) addXScaleComponents(); + if (canScaleDiagonally.Value) addFullScaleComponents(); + if (canScaleY.Value) addYScaleComponents(); if (CanFlipX) addXFlipComponents(); if (CanFlipY) addYFlipComponents(); if (canRotate.Value) addRotationComponents(); diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs index b7c8f16a02..59406b3184 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs @@ -14,9 +14,23 @@ namespace osu.Game.Screens.Edit.Compose.Components public partial class SelectionScaleHandler : Component { /// - /// Whether the scale can currently be performed. + /// Whether horizontal scaling (from the left or right edge) support should be enabled. /// - public Bindable CanScale { get; private set; } = new BindableBool(); + public Bindable CanScaleX { get; private set; } = new BindableBool(); + + /// + /// Whether vertical scaling (from the top or bottom edge) support should be enabled. + /// + public Bindable CanScaleY { get; private set; } = new BindableBool(); + + /// + /// Whether diagonal scaling (from a corner) support should be enabled. + /// + /// + /// There are some cases where we only want to allow proportional resizing, and not allow + /// one or both explicit directions of scale. + /// + public Bindable CanScaleDiagonally { get; private set; } = new BindableBool(); public Quad? OriginalSurroundingQuad { get; protected set; } diff --git a/osu.Game/Utils/GeometryUtils.cs b/osu.Game/Utils/GeometryUtils.cs index ef362d8223..6d8237ea34 100644 --- a/osu.Game/Utils/GeometryUtils.cs +++ b/osu.Game/Utils/GeometryUtils.cs @@ -83,7 +83,7 @@ namespace osu.Game.Utils /// Given a scale multiplier, an origin, and a position, /// will return the scaled position in screen space coordinates. /// - public static Vector2 GetScaledPositionMultiply(Vector2 scale, Vector2 origin, Vector2 position) + public static Vector2 GetScaledPosition(Vector2 scale, Vector2 origin, Vector2 position) { return origin + (position - origin) * scale; } From bc0e6baba70cd9c69dbf9f87180c24c8a47dcff9 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 01:13:05 +0100 Subject: [PATCH 004/581] fix test --- .../Editing/TestSceneComposeSelectBox.cs | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index f6637d0e80..680a76f9b8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -10,9 +10,11 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Utils; using osuTK; using osuTK.Input; @@ -26,9 +28,13 @@ namespace osu.Game.Tests.Visual.Editing [Cached(typeof(SelectionRotationHandler))] private TestSelectionRotationHandler rotationHandler; + [Cached(typeof(SelectionScaleHandler))] + private TestSelectionScaleHandler scaleHandler; + public TestSceneComposeSelectBox() { rotationHandler = new TestSelectionRotationHandler(() => selectionArea); + scaleHandler = new TestSelectionScaleHandler(() => selectionArea); } [SetUp] @@ -45,13 +51,8 @@ namespace osu.Game.Tests.Visual.Editing { RelativeSizeAxes = Axes.Both, - CanScaleX = true, - CanScaleY = true, - CanScaleDiagonally = true, CanFlipX = true, CanFlipY = true, - - OnScale = handleScale } } }; @@ -60,27 +61,6 @@ namespace osu.Game.Tests.Visual.Editing InputManager.ReleaseButton(MouseButton.Left); }); - private bool handleScale(Vector2 amount, Anchor reference) - { - if ((reference & Anchor.y1) == 0) - { - int directionY = (reference & Anchor.y0) > 0 ? -1 : 1; - if (directionY < 0) - selectionArea.Y += amount.Y; - selectionArea.Height += directionY * amount.Y; - } - - if ((reference & Anchor.x1) == 0) - { - int directionX = (reference & Anchor.x0) > 0 ? -1 : 1; - if (directionX < 0) - selectionArea.X += amount.X; - selectionArea.Width += directionX * amount.X; - } - - return true; - } - private partial class TestSelectionRotationHandler : SelectionRotationHandler { private readonly Func getTargetContainer; @@ -125,6 +105,51 @@ namespace osu.Game.Tests.Visual.Editing } } + private partial class TestSelectionScaleHandler : SelectionScaleHandler + { + private readonly Func getTargetContainer; + + public TestSelectionScaleHandler(Func getTargetContainer) + { + this.getTargetContainer = getTargetContainer; + + CanScaleX.Value = true; + CanScaleY.Value = true; + CanScaleDiagonally.Value = true; + } + + [CanBeNull] + private Container targetContainer; + + public override void Begin() + { + if (targetContainer != null) + throw new InvalidOperationException($"Cannot {nameof(Begin)} a scale operation while another is in progress!"); + + targetContainer = getTargetContainer(); + OriginalSurroundingQuad = new Quad(targetContainer!.X, targetContainer.Y, targetContainer.Width, targetContainer.Height); + } + + public override void Update(Vector2 scale, Vector2? origin = null) + { + if (targetContainer == null) + throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); + + Vector2 actualOrigin = origin ?? Vector2.Zero; + + targetContainer.Position = GeometryUtils.GetScaledPosition(scale, actualOrigin, OriginalSurroundingQuad!.Value.TopLeft); + targetContainer.Size = OriginalSurroundingQuad!.Value.Size * scale; + } + + public override void Commit() + { + if (targetContainer == null) + throw new InvalidOperationException($"Cannot {nameof(Commit)} a scale operation without calling {nameof(Begin)} first!"); + + targetContainer = null; + } + } + [Test] public void TestRotationHandleShownOnHover() { From ed430a3df4bbcacf5860db8f21ac625d2a176bbc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 02:49:56 +0100 Subject: [PATCH 005/581] refactor skin editor scale --- .../SkinEditor/SkinSelectionHandler.cs | 157 +------------- .../SkinEditor/SkinSelectionScaleHandler.cs | 198 ++++++++++++++++++ 2 files changed, 204 insertions(+), 151 deletions(-) create mode 100644 osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index efca6f0080..2d8db61ee7 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -7,10 +7,8 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Utils; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -31,148 +29,16 @@ namespace osu.Game.Overlays.SkinEditor UpdatePosition = updateDrawablePosition }; - private bool allSelectedSupportManualSizing(Axes axis) => SelectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false); - - public override bool HandleScale(Vector2 scale, Anchor anchor) + public override SelectionScaleHandler CreateScaleHandler() { - Axes adjustAxis; - - switch (anchor) + var scaleHandler = new SkinSelectionScaleHandler { - // for corners, adjust scale. - case Anchor.TopLeft: - case Anchor.TopRight: - case Anchor.BottomLeft: - case Anchor.BottomRight: - adjustAxis = Axes.Both; - break; + UpdatePosition = updateDrawablePosition + }; - // for edges, adjust size. - // autosize elements can't be easily handled so just disable sizing for now. - case Anchor.TopCentre: - case Anchor.BottomCentre: - if (!allSelectedSupportManualSizing(Axes.Y)) - return false; + scaleHandler.PerformFlipFromScaleHandles += a => SelectionBox.PerformFlipFromScaleHandles(a); - adjustAxis = Axes.Y; - break; - - case Anchor.CentreLeft: - case Anchor.CentreRight: - if (!allSelectedSupportManualSizing(Axes.X)) - return false; - - adjustAxis = Axes.X; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(anchor), anchor, null); - } - - // convert scale to screen space - scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero); - - adjustScaleFromAnchor(ref scale, anchor); - - // the selection quad is always upright, so use an AABB rect to make mutating the values easier. - var selectionRect = getSelectionQuad().AABBFloat; - - // If the selection has no area we cannot scale it - if (selectionRect.Area == 0) - return false; - - // copy to mutate, as we will need to compare to the original later on. - var adjustedRect = selectionRect; - bool isRotated = false; - - // for now aspect lock scale adjustments that occur at corners.. - if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) - { - // project scale vector along diagonal - Vector2 diag = (selectionRect.TopLeft - selectionRect.BottomRight).Normalized(); - scale = Vector2.Dot(scale, diag) * diag; - } - // ..or if any of the selection have been rotated. - // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). - else if (SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation % 90, 0))) - { - isRotated = true; - if (anchor.HasFlagFast(Anchor.x1)) - // if dragging from the horizontal centre, only a vertical component is available. - scale.X = scale.Y / selectionRect.Height * selectionRect.Width; - else - // in all other cases (arbitrarily) use the horizontal component for aspect lock. - scale.Y = scale.X / selectionRect.Width * selectionRect.Height; - } - - if (anchor.HasFlagFast(Anchor.x0)) adjustedRect.X -= scale.X; - if (anchor.HasFlagFast(Anchor.y0)) adjustedRect.Y -= scale.Y; - - // Maintain the selection's centre position if dragging from the centre anchors and selection is rotated. - if (isRotated && anchor.HasFlagFast(Anchor.x1)) adjustedRect.X -= scale.X / 2; - if (isRotated && anchor.HasFlagFast(Anchor.y1)) adjustedRect.Y -= scale.Y / 2; - - adjustedRect.Width += scale.X; - adjustedRect.Height += scale.Y; - - if (adjustedRect.Width <= 0 || adjustedRect.Height <= 0) - { - Axes toFlip = Axes.None; - - if (adjustedRect.Width <= 0) toFlip |= Axes.X; - if (adjustedRect.Height <= 0) toFlip |= Axes.Y; - - SelectionBox.PerformFlipFromScaleHandles(toFlip); - return true; - } - - // scale adjust applied to each individual item should match that of the quad itself. - var scaledDelta = new Vector2( - adjustedRect.Width / selectionRect.Width, - adjustedRect.Height / selectionRect.Height - ); - - foreach (var b in SelectedBlueprints) - { - var drawableItem = (Drawable)b.Item; - - // each drawable's relative position should be maintained in the scaled quad. - var screenPosition = b.ScreenSpaceSelectionPoint; - - var relativePositionInOriginal = - new Vector2( - (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, - (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height - ); - - var newPositionInAdjusted = new Vector2( - adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, - adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y - ); - - updateDrawablePosition(drawableItem, newPositionInAdjusted); - - var currentScaledDelta = scaledDelta; - if (Precision.AlmostEquals(MathF.Abs(drawableItem.Rotation) % 180, 90)) - currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X); - - switch (adjustAxis) - { - case Axes.X: - drawableItem.Width *= currentScaledDelta.X; - break; - - case Axes.Y: - drawableItem.Height *= currentScaledDelta.Y; - break; - - case Axes.Both: - drawableItem.Scale *= currentScaledDelta; - break; - } - } - - return true; + return scaleHandler; } public override bool HandleFlip(Direction direction, bool flipOverOrigin) @@ -410,16 +276,5 @@ namespace osu.Game.Overlays.SkinEditor drawable.Anchor = anchor; drawable.Position -= drawable.AnchorPosition - previousAnchor; } - - private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) - { - // cancel out scale in axes we don't care about (based on which drag handle was used). - if ((reference & Anchor.x1) > 0) scale.X = 0; - if ((reference & Anchor.y1) > 0) scale.Y = 0; - - // reverse the scale direction if dragging from top or left. - if ((reference & Anchor.x0) > 0) scale.X = -scale.X; - if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; - } } } diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs new file mode 100644 index 0000000000..46b39645b2 --- /dev/null +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -0,0 +1,198 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Skinning; +using osu.Game.Utils; +using osuTK; + +namespace osu.Game.Overlays.SkinEditor +{ + public partial class SkinSelectionScaleHandler : SelectionScaleHandler + { + public Action UpdatePosition { get; init; } = null!; + + public event Action? PerformFlipFromScaleHandles; + + [Resolved] + private IEditorChangeHandler? changeHandler { get; set; } + + private BindableList selectedItems { get; } = new BindableList(); + + [BackgroundDependencyLoader] + private void load(SkinEditor skinEditor) + { + selectedItems.BindTo(skinEditor.SelectedComponents); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedItems.CollectionChanged += (_, __) => updateState(); + updateState(); + } + + private void updateState() + { + CanScaleX.Value = allSelectedSupportManualSizing(Axes.X); + CanScaleY.Value = allSelectedSupportManualSizing(Axes.Y); + CanScaleDiagonally.Value = true; + } + + private bool allSelectedSupportManualSizing(Axes axis) => selectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false); + + private Drawable[]? objectsInScale; + + private Vector2? defaultOrigin; + private Dictionary? originalWidths; + private Dictionary? originalHeights; + private Dictionary? originalScales; + private Dictionary? originalPositions; + + public override void Begin() + { + if (objectsInScale != null) + throw new InvalidOperationException($"Cannot {nameof(Begin)} a scale operation while another is in progress!"); + + changeHandler?.BeginChange(); + + objectsInScale = selectedItems.Cast().ToArray(); + originalWidths = objectsInScale.ToDictionary(d => d, d => d.Width); + originalHeights = objectsInScale.ToDictionary(d => d, d => d.Height); + originalScales = objectsInScale.ToDictionary(d => d, d => d.Scale); + originalPositions = objectsInScale.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition)); + OriginalSurroundingQuad = GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())); + defaultOrigin = OriginalSurroundingQuad.Value.Centre; + } + + public override void Update(Vector2 scale, Vector2? origin = null) + { + if (objectsInScale == null) + throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); + + Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); + + var actualOrigin = origin ?? defaultOrigin.Value; + + Axes adjustAxis = scale.X == 0 ? Axes.Y : scale.Y == 0 ? Axes.X : Axes.Both; + + if ((adjustAxis == Axes.Y && !allSelectedSupportManualSizing(Axes.Y)) || + (adjustAxis == Axes.X && !allSelectedSupportManualSizing(Axes.X))) + return; + + // the selection quad is always upright, so use an AABB rect to make mutating the values easier. + var selectionRect = OriginalSurroundingQuad.Value.AABBFloat; + + // If the selection has no area we cannot scale it + if (selectionRect.Area == 0) + return; + + // copy to mutate, as we will need to compare to the original later on. + var adjustedRect = selectionRect; + + // for now aspect lock scale adjustments that occur at corners.. + if (adjustAxis == Axes.Both) + { + // project scale vector along diagonal + Vector2 diag = new Vector2(1, 1).Normalized(); + scale = Vector2.Dot(scale, diag) * diag; + } + // ..or if any of the selection have been rotated. + // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). + else if (objectsInScale.Any(b => !Precision.AlmostEquals(b.Rotation % 90, 0))) + { + if (adjustAxis == Axes.Y) + // if dragging from the horizontal centre, only a vertical component is available. + scale.X = scale.Y / selectionRect.Height * selectionRect.Width; + else + // in all other cases (arbitrarily) use the horizontal component for aspect lock. + scale.Y = scale.X / selectionRect.Width * selectionRect.Height; + } + + adjustedRect.Location = GeometryUtils.GetScaledPosition(scale, actualOrigin, OriginalSurroundingQuad!.Value.TopLeft); + adjustedRect.Size = OriginalSurroundingQuad!.Value.Size * scale; + + if (adjustedRect.Width <= 0 || adjustedRect.Height <= 0) + { + Axes toFlip = Axes.None; + + if (adjustedRect.Width <= 0) toFlip |= Axes.X; + if (adjustedRect.Height <= 0) toFlip |= Axes.Y; + + PerformFlipFromScaleHandles?.Invoke(toFlip); + return; + } + + // scale adjust applied to each individual item should match that of the quad itself. + var scaledDelta = new Vector2( + adjustedRect.Width / selectionRect.Width, + adjustedRect.Height / selectionRect.Height + ); + + foreach (var b in objectsInScale) + { + // each drawable's relative position should be maintained in the scaled quad. + var screenPosition = originalPositions[b]; + + var relativePositionInOriginal = + new Vector2( + (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, + (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height + ); + + var newPositionInAdjusted = new Vector2( + adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, + adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y + ); + + UpdatePosition(b, newPositionInAdjusted); + + var currentScaledDelta = scaledDelta; + if (Precision.AlmostEquals(MathF.Abs(b.Rotation) % 180, 90)) + currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X); + + switch (adjustAxis) + { + case Axes.X: + b.Width = originalWidths[b] * currentScaledDelta.X; + break; + + case Axes.Y: + b.Height = originalHeights[b] * currentScaledDelta.Y; + break; + + case Axes.Both: + b.Scale = originalScales[b] * currentScaledDelta; + break; + } + } + } + + public override void Commit() + { + if (objectsInScale == null) + throw new InvalidOperationException($"Cannot {nameof(Commit)} a scale operation without calling {nameof(Begin)} first!"); + + changeHandler?.EndChange(); + + objectsInScale = null; + originalPositions = null; + originalWidths = null; + originalHeights = null; + originalScales = null; + defaultOrigin = null; + } + } +} From 6a57be0a50c8ddc20356f237dd80bc219226ba59 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 13:04:05 +0100 Subject: [PATCH 006/581] clean up code and fix flipping --- .../SkinEditor/SkinSelectionScaleHandler.cs | 74 ++++++++----------- .../Components/SelectionBoxScaleHandle.cs | 19 +++-- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index 46b39645b2..c2f788a9e8 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -61,6 +61,9 @@ namespace osu.Game.Overlays.SkinEditor private Dictionary? originalScales; private Dictionary? originalPositions; + private bool isFlippedX; + private bool isFlippedY; + public override void Begin() { if (objectsInScale != null) @@ -75,6 +78,9 @@ namespace osu.Game.Overlays.SkinEditor originalPositions = objectsInScale.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition)); OriginalSurroundingQuad = GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())); defaultOrigin = OriginalSurroundingQuad.Value.Centre; + + isFlippedX = false; + isFlippedY = false; } public override void Update(Vector2 scale, Vector2? origin = null) @@ -85,29 +91,21 @@ namespace osu.Game.Overlays.SkinEditor Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); var actualOrigin = origin ?? defaultOrigin.Value; - Axes adjustAxis = scale.X == 0 ? Axes.Y : scale.Y == 0 ? Axes.X : Axes.Both; if ((adjustAxis == Axes.Y && !allSelectedSupportManualSizing(Axes.Y)) || (adjustAxis == Axes.X && !allSelectedSupportManualSizing(Axes.X))) return; - // the selection quad is always upright, so use an AABB rect to make mutating the values easier. - var selectionRect = OriginalSurroundingQuad.Value.AABBFloat; - // If the selection has no area we cannot scale it - if (selectionRect.Area == 0) + if (OriginalSurroundingQuad.Value.Width == 0 || OriginalSurroundingQuad.Value.Height == 0) return; - // copy to mutate, as we will need to compare to the original later on. - var adjustedRect = selectionRect; - // for now aspect lock scale adjustments that occur at corners.. if (adjustAxis == Axes.Both) { // project scale vector along diagonal - Vector2 diag = new Vector2(1, 1).Normalized(); - scale = Vector2.Dot(scale, diag) * diag; + scale = new Vector2((scale.X + scale.Y) * 0.5f); } // ..or if any of the selection have been rotated. // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). @@ -115,66 +113,54 @@ namespace osu.Game.Overlays.SkinEditor { if (adjustAxis == Axes.Y) // if dragging from the horizontal centre, only a vertical component is available. - scale.X = scale.Y / selectionRect.Height * selectionRect.Width; + scale.X = scale.Y; else // in all other cases (arbitrarily) use the horizontal component for aspect lock. - scale.Y = scale.X / selectionRect.Width * selectionRect.Height; + scale.Y = scale.X; } - adjustedRect.Location = GeometryUtils.GetScaledPosition(scale, actualOrigin, OriginalSurroundingQuad!.Value.TopLeft); - adjustedRect.Size = OriginalSurroundingQuad!.Value.Size * scale; + bool flippedX = scale.X < 0; + bool flippedY = scale.Y < 0; + Axes toFlip = Axes.None; - if (adjustedRect.Width <= 0 || adjustedRect.Height <= 0) + if (flippedX != isFlippedX) { - Axes toFlip = Axes.None; + isFlippedX = flippedX; + toFlip |= Axes.X; + } - if (adjustedRect.Width <= 0) toFlip |= Axes.X; - if (adjustedRect.Height <= 0) toFlip |= Axes.Y; + if (flippedY != isFlippedY) + { + isFlippedY = flippedY; + toFlip |= Axes.Y; + } + if (toFlip != Axes.None) + { PerformFlipFromScaleHandles?.Invoke(toFlip); return; } - // scale adjust applied to each individual item should match that of the quad itself. - var scaledDelta = new Vector2( - adjustedRect.Width / selectionRect.Width, - adjustedRect.Height / selectionRect.Height - ); - foreach (var b in objectsInScale) { - // each drawable's relative position should be maintained in the scaled quad. - var screenPosition = originalPositions[b]; + UpdatePosition(b, GeometryUtils.GetScaledPosition(scale, actualOrigin, originalPositions[b])); - var relativePositionInOriginal = - new Vector2( - (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, - (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height - ); - - var newPositionInAdjusted = new Vector2( - adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, - adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y - ); - - UpdatePosition(b, newPositionInAdjusted); - - var currentScaledDelta = scaledDelta; + var currentScale = scale; if (Precision.AlmostEquals(MathF.Abs(b.Rotation) % 180, 90)) - currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X); + currentScale = new Vector2(scale.Y, scale.X); switch (adjustAxis) { case Axes.X: - b.Width = originalWidths[b] * currentScaledDelta.X; + b.Width = originalWidths[b] * currentScale.X; break; case Axes.Y: - b.Height = originalHeights[b] * currentScaledDelta.Y; + b.Height = originalHeights[b] * currentScale.Y; break; case Axes.Both: - b.Scale = originalScales[b] * currentScaledDelta; + b.Scale = originalScales[b] * currentScale; break; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 56c5585ae7..6179be1d4f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Logging; using osuTK; using osuTK.Input; @@ -24,6 +25,8 @@ namespace osu.Game.Screens.Edit.Compose.Components Size = new Vector2(10); } + private Anchor originalAnchor; + protected override bool OnDragStart(DragStartEvent e) { if (e.Button != MouseButton.Left) @@ -31,6 +34,8 @@ namespace osu.Game.Screens.Edit.Compose.Components if (scaleHandler == null) return false; + originalAnchor = Anchor; + scaleHandler.Begin(); return true; } @@ -40,10 +45,10 @@ namespace osu.Game.Screens.Edit.Compose.Components var quad = scaleHandler!.OriginalSurroundingQuad!.Value; Vector2 origin = quad.TopLeft; - if ((Anchor & Anchor.x0) > 0) + if ((originalAnchor & Anchor.x0) > 0) origin.X += quad.Width; - if ((Anchor & Anchor.y0) > 0) + if ((originalAnchor & Anchor.y0) > 0) origin.Y += quad.Height; return origin; @@ -89,6 +94,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Vector2 convertDragEventToScaleMultiplier(DragEvent e) { Vector2 scale = e.MousePosition - e.MouseDownPosition; + Logger.Log($"Raw scale {scale}"); adjustScaleFromAnchor(ref scale); return Vector2.Divide(scale, scaleHandler!.OriginalSurroundingQuad!.Value.Size) + Vector2.One; } @@ -96,12 +102,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void adjustScaleFromAnchor(ref Vector2 scale) { // cancel out scale in axes we don't care about (based on which drag handle was used). - if ((Anchor & Anchor.x1) > 0) scale.X = 1; - if ((Anchor & Anchor.y1) > 0) scale.Y = 1; + if ((originalAnchor & Anchor.x1) > 0) scale.X = 1; + if ((originalAnchor & Anchor.y1) > 0) scale.Y = 1; // reverse the scale direction if dragging from top or left. - if ((Anchor & Anchor.x0) > 0) scale.X = -scale.X; - if ((Anchor & Anchor.y0) > 0) scale.Y = -scale.Y; + if ((originalAnchor & Anchor.x0) > 0) scale.X = -scale.X; + if ((originalAnchor & Anchor.y0) > 0) scale.Y = -scale.Y; } private void applyScale(bool shouldKeepAspectRatio) @@ -110,6 +116,7 @@ namespace osu.Game.Screens.Edit.Compose.Components ? new Vector2(MathF.Max(rawScale.X, rawScale.Y)) : rawScale; + Logger.Log($"Raw scale adjusted {newScale}, origin {getOriginPosition()}"); scaleHandler!.Update(newScale, getOriginPosition()); } } From fcaa5ec20e3fe43948bb1bd9d898d45dcf9b50cf Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 13:26:08 +0100 Subject: [PATCH 007/581] remove debug logs --- .../Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 6179be1d4f..e0b41fd8e2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; -using osu.Framework.Logging; using osuTK; using osuTK.Input; @@ -94,7 +93,6 @@ namespace osu.Game.Screens.Edit.Compose.Components private Vector2 convertDragEventToScaleMultiplier(DragEvent e) { Vector2 scale = e.MousePosition - e.MouseDownPosition; - Logger.Log($"Raw scale {scale}"); adjustScaleFromAnchor(ref scale); return Vector2.Divide(scale, scaleHandler!.OriginalSurroundingQuad!.Value.Size) + Vector2.One; } @@ -116,7 +114,6 @@ namespace osu.Game.Screens.Edit.Compose.Components ? new Vector2(MathF.Max(rawScale.X, rawScale.Y)) : rawScale; - Logger.Log($"Raw scale adjusted {newScale}, origin {getOriginPosition()}"); scaleHandler!.Update(newScale, getOriginPosition()); } } From e1f3f7d988194e2f48df3aba184f095f59d2623b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 14:49:47 +0100 Subject: [PATCH 008/581] fix possible NaN in clamped scale --- .../Edit/OsuSelectionScaleHandler.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 7b0ae947e7..3c4818a533 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -177,10 +177,14 @@ namespace osu.Game.Rulesets.Osu.Edit var br1 = Vector2.Divide(-origin, selectionQuad.BottomRight - origin); var br2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - origin, selectionQuad.BottomRight - origin); - scale.X = selectionQuad.TopLeft.X - origin.X < 0 ? MathHelper.Clamp(scale.X, tl2.X, tl1.X) : MathHelper.Clamp(scale.X, tl1.X, tl2.X); - scale.Y = selectionQuad.TopLeft.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, tl2.Y, tl1.Y) : MathHelper.Clamp(scale.Y, tl1.Y, tl2.Y); - scale.X = selectionQuad.BottomRight.X - origin.X < 0 ? MathHelper.Clamp(scale.X, br2.X, br1.X) : MathHelper.Clamp(scale.X, br1.X, br2.X); - scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y); + if (!Precision.AlmostEquals(selectionQuad.TopLeft.X - origin.X, 0)) + scale.X = selectionQuad.TopLeft.X - origin.X < 0 ? MathHelper.Clamp(scale.X, tl2.X, tl1.X) : MathHelper.Clamp(scale.X, tl1.X, tl2.X); + if (!Precision.AlmostEquals(selectionQuad.TopLeft.Y - origin.Y, 0)) + scale.Y = selectionQuad.TopLeft.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, tl2.Y, tl1.Y) : MathHelper.Clamp(scale.Y, tl1.Y, tl2.Y); + if (!Precision.AlmostEquals(selectionQuad.BottomRight.X - origin.X, 0)) + scale.X = selectionQuad.BottomRight.X - origin.X < 0 ? MathHelper.Clamp(scale.X, br2.X, br1.X) : MathHelper.Clamp(scale.X, br1.X, br2.X); + if (!Precision.AlmostEquals(selectionQuad.BottomRight.Y - origin.Y, 0)) + scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y); return scale; } From 6a4129dad880e839b033d77ac2bdb00f22dc1c0d Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:11:35 +0100 Subject: [PATCH 009/581] fix aspect ratio transform --- .../Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index e0b41fd8e2..ea98ac573c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void applyScale(bool shouldKeepAspectRatio) { var newScale = shouldKeepAspectRatio - ? new Vector2(MathF.Max(rawScale.X, rawScale.Y)) + ? new Vector2((rawScale.X + rawScale.Y) * 0.5f) : rawScale; scaleHandler!.Update(newScale, getOriginPosition()); From 0fc448f4f3a31643903017a9881b81749561e0eb Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:12:48 +0100 Subject: [PATCH 010/581] fix adjusting scale from anchor --- osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs | 2 +- .../Edit/Compose/Components/SelectionBoxScaleHandle.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index c2f788a9e8..bf75469d7a 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.SkinEditor Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); var actualOrigin = origin ?? defaultOrigin.Value; - Axes adjustAxis = scale.X == 0 ? Axes.Y : scale.Y == 0 ? Axes.X : Axes.Both; + Axes adjustAxis = scale.X == 1 ? Axes.Y : scale.Y == 1 ? Axes.X : Axes.Both; if ((adjustAxis == Axes.Y && !allSelectedSupportManualSizing(Axes.Y)) || (adjustAxis == Axes.X && !allSelectedSupportManualSizing(Axes.X))) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index ea98ac573c..60fbeb9fff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -100,8 +100,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private void adjustScaleFromAnchor(ref Vector2 scale) { // cancel out scale in axes we don't care about (based on which drag handle was used). - if ((originalAnchor & Anchor.x1) > 0) scale.X = 1; - if ((originalAnchor & Anchor.y1) > 0) scale.Y = 1; + if ((originalAnchor & Anchor.x1) > 0) scale.X = 0; + if ((originalAnchor & Anchor.y1) > 0) scale.Y = 0; // reverse the scale direction if dragging from top or left. if ((originalAnchor & Anchor.x0) > 0) scale.X = -scale.X; From 1596776a81b91db1850bf3325b6d2992ed5eaf6c Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:15:49 +0100 Subject: [PATCH 011/581] fix imports --- osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs | 1 + .../Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 3c4818a533..1e3e22e34a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Primitives; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 60fbeb9fff..3dde97657f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; From 9b9485f656807570afd91bd3b25923147a2075f2 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:39:38 +0100 Subject: [PATCH 012/581] fix adjust axes detection --- .../Edit/OsuSelectionScaleHandler.cs | 3 +- .../SkinEditor/SkinSelectionScaleHandler.cs | 3 +- .../Components/SelectionBoxScaleHandle.cs | 47 +++++++++++++------ .../Components/SelectionScaleHandler.cs | 8 ++-- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 1e3e22e34a..7d5240fb69 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osu.Framework.Utils; using osu.Game.Rulesets.Edit; @@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Edit obj => obj.Path.ControlPoints.Select(p => p.Type).ToArray()); } - public override void Update(Vector2 scale, Vector2? origin = null) + public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) { if (objectsInScale == null) throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index bf75469d7a..0bd146a0a1 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.SkinEditor isFlippedY = false; } - public override void Update(Vector2 scale, Vector2? origin = null) + public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) { if (objectsInScale == null) throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); @@ -91,7 +91,6 @@ namespace osu.Game.Overlays.SkinEditor Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); var actualOrigin = origin ?? defaultOrigin.Value; - Axes adjustAxis = scale.X == 1 ? Axes.Y : scale.Y == 1 ? Axes.X : Axes.Both; if ((adjustAxis == Axes.Y && !allSelectedSupportManualSizing(Axes.Y)) || (adjustAxis == Axes.X && !allSelectedSupportManualSizing(Axes.X))) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 3dde97657f..d433e4e860 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -38,20 +38,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } - private Vector2 getOriginPosition() - { - var quad = scaleHandler!.OriginalSurroundingQuad!.Value; - Vector2 origin = quad.TopLeft; - - if ((originalAnchor & Anchor.x0) > 0) - origin.X += quad.Width; - - if ((originalAnchor & Anchor.y0) > 0) - origin.Y += quad.Height; - - return origin; - } - private Vector2 rawScale; protected override void OnDrag(DragEvent e) @@ -113,7 +99,38 @@ namespace osu.Game.Screens.Edit.Compose.Components ? new Vector2((rawScale.X + rawScale.Y) * 0.5f) : rawScale; - scaleHandler!.Update(newScale, getOriginPosition()); + scaleHandler!.Update(newScale, getOriginPosition(), getAdjustAxis()); + } + + private Vector2 getOriginPosition() + { + var quad = scaleHandler!.OriginalSurroundingQuad!.Value; + Vector2 origin = quad.TopLeft; + + if ((originalAnchor & Anchor.x0) > 0) + origin.X += quad.Width; + + if ((originalAnchor & Anchor.y0) > 0) + origin.Y += quad.Height; + + return origin; + } + + private Axes getAdjustAxis() + { + switch (originalAnchor) + { + case Anchor.TopCentre: + case Anchor.BottomCentre: + return Axes.Y; + + case Anchor.CentreLeft: + case Anchor.CentreRight: + return Axes.X; + + default: + return Axes.Both; + } } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs index 59406b3184..a96f627e56 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionScaleHandler.cs @@ -46,10 +46,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The origin point to scale from. /// If the default value is supplied, a sane implementation-defined default will be used. /// - public void ScaleSelection(Vector2 scale, Vector2? origin = null) + /// The axes to adjust the scale in. + public void ScaleSelection(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) { Begin(); - Update(scale, origin); + Update(scale, origin, adjustAxis); Commit(); } @@ -83,7 +84,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The origin point to scale from. /// If the default value is supplied, a sane implementation-defined default will be used. /// - public virtual void Update(Vector2 scale, Vector2? origin = null) + /// The axes to adjust the scale in. + public virtual void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) { } From ac76af5cc8f894dfb87ae4d4987172b9f5a85934 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:43:47 +0100 Subject: [PATCH 013/581] fix skin scale coordinate system --- osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index 0bd146a0a1..e87952efa0 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.SkinEditor originalHeights = objectsInScale.ToDictionary(d => d, d => d.Height); originalScales = objectsInScale.ToDictionary(d => d, d => d.Scale); originalPositions = objectsInScale.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition)); - OriginalSurroundingQuad = GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())); + OriginalSurroundingQuad = ToLocalSpace(GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray()))); defaultOrigin = OriginalSurroundingQuad.Value.Centre; isFlippedX = false; @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.SkinEditor Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); - var actualOrigin = origin ?? defaultOrigin.Value; + var actualOrigin = ToScreenSpace(origin ?? defaultOrigin.Value); if ((adjustAxis == Axes.Y && !allSelectedSupportManualSizing(Axes.Y)) || (adjustAxis == Axes.X && !allSelectedSupportManualSizing(Axes.X))) From 9459c66981a022905283b603f9bfb0d7e3cf6e77 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 15:53:08 +0100 Subject: [PATCH 014/581] fix test --- osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index 680a76f9b8..4c60ecf5db 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Editing OriginalSurroundingQuad = new Quad(targetContainer!.X, targetContainer.Y, targetContainer.Width, targetContainer.Height); } - public override void Update(Vector2 scale, Vector2? origin = null) + public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) { if (targetContainer == null) throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); From a155b315bf8ad9060ae214ccc8763e4deebdae6c Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 16:10:17 +0100 Subject: [PATCH 015/581] Fix negative width or height skin drawables --- osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index e87952efa0..8daf0043da 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -151,11 +151,11 @@ namespace osu.Game.Overlays.SkinEditor switch (adjustAxis) { case Axes.X: - b.Width = originalWidths[b] * currentScale.X; + b.Width = MathF.Abs(originalWidths[b] * currentScale.X); break; case Axes.Y: - b.Height = originalHeights[b] * currentScale.Y; + b.Height = MathF.Abs(originalHeights[b] * currentScale.Y); break; case Axes.Both: From 5f40d3aed9ca859535c75d8f9e927d5cc7ad1581 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 16:29:26 +0100 Subject: [PATCH 016/581] rename variable --- .../Edit/Compose/Components/SelectionBoxScaleHandle.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index d433e4e860..74629a5384 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -48,14 +48,14 @@ namespace osu.Game.Screens.Edit.Compose.Components rawScale = convertDragEventToScaleMultiplier(e); - applyScale(shouldKeepAspectRatio: e.ShiftPressed); + applyScale(shouldLockAspectRatio: e.ShiftPressed); } protected override bool OnKeyDown(KeyDownEvent e) { if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) { - applyScale(shouldKeepAspectRatio: true); + applyScale(shouldLockAspectRatio: true); return true; } @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Edit.Compose.Components base.OnKeyUp(e); if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) - applyScale(shouldKeepAspectRatio: false); + applyScale(shouldLockAspectRatio: false); } protected override void OnDragEnd(DragEndEvent e) @@ -93,9 +93,9 @@ namespace osu.Game.Screens.Edit.Compose.Components if ((originalAnchor & Anchor.y0) > 0) scale.Y = -scale.Y; } - private void applyScale(bool shouldKeepAspectRatio) + private void applyScale(bool shouldLockAspectRatio) { - var newScale = shouldKeepAspectRatio + var newScale = shouldLockAspectRatio ? new Vector2((rawScale.X + rawScale.Y) * 0.5f) : rawScale; From 2f924b33686ff7d7cb3080c2cc16f891d27cbc2e Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 16:33:03 +0100 Subject: [PATCH 017/581] fix skewed single axis scale --- .../SkinEditor/SkinSelectionScaleHandler.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index 8daf0043da..0c2ee6aae3 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -100,23 +100,15 @@ namespace osu.Game.Overlays.SkinEditor if (OriginalSurroundingQuad.Value.Width == 0 || OriginalSurroundingQuad.Value.Height == 0) return; - // for now aspect lock scale adjustments that occur at corners.. + // for now aspect lock scale adjustments that occur at corners. if (adjustAxis == Axes.Both) { // project scale vector along diagonal scale = new Vector2((scale.X + scale.Y) * 0.5f); } - // ..or if any of the selection have been rotated. - // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). - else if (objectsInScale.Any(b => !Precision.AlmostEquals(b.Rotation % 90, 0))) - { - if (adjustAxis == Axes.Y) - // if dragging from the horizontal centre, only a vertical component is available. - scale.X = scale.Y; - else - // in all other cases (arbitrarily) use the horizontal component for aspect lock. - scale.Y = scale.X; - } + // If any of the selection have been rotated and the adjust axis is not both, + // we would require skew logic to achieve a correct image editor-like scale. + // For now we just ignore, because it would likely not be the user's expected transform anyway. bool flippedX = scale.X < 0; bool flippedY = scale.Y < 0; From 78e87d379b760b9ebd5d567610423b607013b16d Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 20 Jan 2024 16:49:10 +0100 Subject: [PATCH 018/581] fix divide by zero --- .../Edit/Compose/Components/SelectionBoxScaleHandle.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 74629a5384..a1f6a1732a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osuTK; using osuTK.Input; @@ -79,7 +80,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { Vector2 scale = e.MousePosition - e.MouseDownPosition; adjustScaleFromAnchor(ref scale); - return Vector2.Divide(scale, scaleHandler!.OriginalSurroundingQuad!.Value.Size) + Vector2.One; + + var surroundingQuad = scaleHandler!.OriginalSurroundingQuad!.Value; + scale.X = Precision.AlmostEquals(surroundingQuad.Width, 0) ? 0 : scale.X / surroundingQuad.Width; + scale.Y = Precision.AlmostEquals(surroundingQuad.Height, 0) ? 0 : scale.Y / surroundingQuad.Height; + + return scale + Vector2.One; } private void adjustScaleFromAnchor(ref Vector2 scale) From e1f8bc96924b104665782aacfd223eb2ba9dafed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 12:09:39 +0700 Subject: [PATCH 019/581] Rename CanRotate property of SelectionRotationHandler to a more descriptive name --- osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs | 4 +++- osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs | 2 +- osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs | 2 +- osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs | 2 +- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 2 +- .../Edit/Compose/Components/SelectionRotationHandler.cs | 5 +++-- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs index 21fb8a67de..0ce78e4f61 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSelectionRotationHandler : SelectionRotationHandler { + public BindableBool CanRotatePlayfieldOrigin { get; private set; } = new(); [Resolved] private IEditorChangeHandler? changeHandler { get; set; } @@ -41,7 +42,8 @@ namespace osu.Game.Rulesets.Osu.Edit private void updateState() { var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects); - CanRotate.Value = quad.Width > 0 || quad.Height > 0; + CanRotateSelectionOrigin.Value = quad.Width > 0 || quad.Height > 0; + CanRotatePlayfieldOrigin.Value = selectedItems.Any(); } private OsuHitObject[]? objectsInRotation; diff --git a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs index 3da9f5b69b..291c79e613 100644 --- a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Edit // bindings to `Enabled` on the buttons are decoupled on purpose // due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set. - canRotate.BindTo(RotationHandler.CanRotate); + canRotate.BindTo(RotationHandler.CanRotateSelectionOrigin); canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index f6637d0e80..8e4f4a1cfd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Editing { this.getTargetContainer = getTargetContainer; - CanRotate.Value = true; + CanRotateSelectionOrigin.Value = true; } [CanBeNull] diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs index 60f69000a2..7ecf116b68 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionRotationHandler.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.SkinEditor private void updateState() { - CanRotate.Value = selectedItems.Count > 0; + CanRotateSelectionOrigin.Value = selectedItems.Count > 0; } private Drawable[]? objectsInRotation; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 0b16941bc4..85ea7364e8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void load() { if (rotationHandler != null) - canRotate.BindTo(rotationHandler.CanRotate); + canRotate.BindTo(rotationHandler.CanRotateSelectionOrigin); canRotate.BindValueChanged(_ => recreate(), true); } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs index 5faa4a108d..749e1aab17 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs @@ -13,9 +13,10 @@ namespace osu.Game.Screens.Edit.Compose.Components public partial class SelectionRotationHandler : Component { /// - /// Whether the rotation can currently be performed. + /// Whether rotation anchored by the selection origin can currently be performed. + /// This is in constrast to rotation anchored by the entire field. /// - public Bindable CanRotate { get; private set; } = new BindableBool(); + public Bindable CanRotateSelectionOrigin { get; private set; } = new BindableBool(); /// /// Performs a single, instant, atomic rotation operation. From 601ba9f194ee029714d85a73f4e0078a7401f910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 12:16:35 +0700 Subject: [PATCH 020/581] Change rotate tool button to be enabled on single circle. Inject osu ruleset specific rotate handler instead of generic handler. --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 5 ++++- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 +- osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 448cfaf84c..a0fb0b06c3 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -101,7 +101,10 @@ namespace osu.Game.Rulesets.Osu.Edit RightToolbox.AddRange(new EditorToolboxGroup[] { - new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }, + new TransformToolboxGroup + { + RotationHandler = (OsuSelectionRotationHandler)BlueprintContainer.SelectionHandler.RotationHandler, + }, FreehandlSliderToolboxGroup } ); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index cea2adc6e2..7e645bc670 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Osu.Edit if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; } - public override SelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(); + public override OsuSelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(); private void scaleSlider(Slider slider, Vector2 scale) { diff --git a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs index 291c79e613..c70f35c6fb 100644 --- a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Components; -using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Rulesets.Osu.Edit @@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit private EditorToolButton rotateButton = null!; - public SelectionRotationHandler RotationHandler { get; init; } = null!; + public OsuSelectionRotationHandler RotationHandler { get; init; } = null!; public TransformToolboxGroup() : base("transform") @@ -53,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Edit // bindings to `Enabled` on the buttons are decoupled on purpose // due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set. - canRotate.BindTo(RotationHandler.CanRotateSelectionOrigin); + canRotate.BindTo(RotationHandler.CanRotatePlayfieldOrigin); canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true); } From 500bed01215f6b99eeb3a6a717563b243c18ccd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 14:24:35 +0700 Subject: [PATCH 021/581] Split editor toolbox radio button disabling logic from EditorRadioButton, then add disabling logic for rotate popover --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 8 +++++++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 8 ++++++++ .../Edit/Components/RadioButtons/EditorRadioButton.cs | 2 -- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index f09d6b78e6..fdab84f38d 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit private SliderWithTextBoxInput angleInput = null!; private EditorRadioButtonCollection rotationOrigin = null!; + private RadioButton selectionCentreButton = null!; public PreciseRotationPopover(SelectionRotationHandler rotationHandler) { this.rotationHandler = rotationHandler; @@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit new RadioButton("Playfield centre", () => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.PlayfieldCentre }, () => new SpriteIcon { Icon = FontAwesome.Regular.Square }), - new RadioButton("Selection centre", + selectionCentreButton = new RadioButton("Selection centre", () => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.SelectionCentre }, () => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare }) } @@ -76,6 +77,11 @@ namespace osu.Game.Rulesets.Osu.Edit angleInput.Current.BindValueChanged(angle => rotationInfo.Value = rotationInfo.Value with { Degrees = angle.NewValue }); rotationOrigin.Items.First().Select(); + rotationHandler.CanRotateSelectionOrigin.BindValueChanged(e => + { + selectionCentreButton.Selected.Disabled = !e.NewValue; + }, true); + rotationInfo.BindValueChanged(rotation => { rotationHandler.Update(rotation.NewValue.Degrees, rotation.NewValue.Origin == RotationOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 50e6393895..6abc6cb95b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -244,6 +244,14 @@ namespace osu.Game.Rulesets.Edit if (!timing.NewValue) setSelectTool(); }); + + EditorBeatmap.HasTiming.BindValueChanged(hasTiming => + { + foreach (var item in toolboxCollection.Items) + { + item.Selected.Disabled = !hasTiming.NewValue; + } + }, true); } protected override void Update() diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index 65f3e41c13..5549095639 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -76,8 +76,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons Selected?.Invoke(Button); }; - editorBeatmap?.HasTiming.BindValueChanged(hasTiming => Button.Selected.Disabled = !hasTiming.NewValue, true); - Button.Selected.BindDisabledChanged(disabled => Enabled.Value = !disabled, true); updateSelectionState(); } From 94ada87cbad0828fcb669d0e4133f850b12a07b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 14:24:45 +0700 Subject: [PATCH 022/581] Un-hardcode tooltip from EditorRadioButton and add disabled tooltip for rotation popover --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 2 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +++++ .../Edit/Components/RadioButtons/EditorRadioButton.cs | 2 +- .../Edit/Components/RadioButtons/RadioButton.cs | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index fdab84f38d..2cf6799279 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit private EditorRadioButtonCollection rotationOrigin = null!; private RadioButton selectionCentreButton = null!; + public PreciseRotationPopover(SelectionRotationHandler rotationHandler) { this.rotationHandler = rotationHandler; @@ -67,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit } } }; + selectionCentreButton.TooltipTextWhenDisabled = "We can't rotate a circle around itself! Can we?"; } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 6abc6cb95b..bc8de7f4b2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -212,6 +212,11 @@ namespace osu.Game.Rulesets.Edit .Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon)) .ToList(); + foreach (var item in toolboxCollection.Items) + { + item.TooltipTextWhenDisabled = "Add at least one timing point first!"; + } + TernaryStates = CreateTernaryButtons().ToArray(); togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b))); diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index 5549095639..601548fadd 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -97,6 +97,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons X = 40f }; - public LocalisableString TooltipText => Enabled.Value ? string.Empty : "Add at least one timing point first!"; + public LocalisableString TooltipText => Enabled.Value ? Button.TooltipTextWhenEnabled : Button.TooltipTextWhenDisabled; } } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index 9dcd29bf83..1b47c028ab 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; namespace osu.Game.Screens.Edit.Components.RadioButtons { @@ -11,9 +12,19 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons { /// /// Whether this is selected. + /// Disable this bindable to disable the button. /// public readonly BindableBool Selected; + /// + /// Tooltip text that will be shown on hover if button is enabled. + /// + public LocalisableString TooltipTextWhenEnabled { get; set; } = string.Empty; + /// + /// Tooltip text that will be shown on hover if button is disabled. + /// + public LocalisableString TooltipTextWhenDisabled { get; set; } = string.Empty; + /// /// The item related to this button. /// From b87ff4db0d5d69e7f8e787eb1135745c491c074e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 15:33:48 +0700 Subject: [PATCH 023/581] Edit test for precise rotation popover --- .../Editor/TestScenePreciseRotation.cs | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs index d7dd30d608..67283f40da 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs @@ -24,14 +24,38 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestHotkeyHandling() { - AddStep("select single circle", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType().First())); + AddStep("deselect everything", () => EditorBeatmap.SelectedHitObjects.Clear()); AddStep("press rotate hotkey", () => { InputManager.PressKey(Key.ControlLeft); InputManager.Key(Key.R); InputManager.ReleaseKey(Key.ControlLeft); }); - AddUntilStep("no popover present", () => this.ChildrenOfType().Count(), () => Is.Zero); + AddUntilStep("no popover present", getPopover, () => Is.Null); + + AddStep("select single circle", + () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType().First())); + AddStep("press rotate hotkey", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ControlLeft); + }); + AddUntilStep("popover present", getPopover, () => Is.Not.Null); + AddAssert("only playfield centre origin rotation available", () => + { + var popover = getPopover(); + var buttons = popover.ChildrenOfType(); + return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value is false) && + buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value is true); + }); + AddStep("press rotate hotkey", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ControlLeft); + }); + AddUntilStep("no popover present", getPopover, () => Is.Null); AddStep("select first three objects", () => { @@ -44,14 +68,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.Key(Key.R); InputManager.ReleaseKey(Key.ControlLeft); }); - AddUntilStep("popover present", () => this.ChildrenOfType().Count(), () => Is.EqualTo(1)); + AddUntilStep("popover present", getPopover, () => Is.Not.Null); + AddAssert("both origin rotation available", () => + { + var popover = getPopover(); + var buttons = popover.ChildrenOfType(); + return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value is true) && + buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value is true); + }); AddStep("press rotate hotkey", () => { InputManager.PressKey(Key.ControlLeft); InputManager.Key(Key.R); InputManager.ReleaseKey(Key.ControlLeft); }); - AddUntilStep("no popover present", () => this.ChildrenOfType().Count(), () => Is.Zero); + AddUntilStep("no popover present", getPopover, () => Is.Null); + + PreciseRotationPopover? getPopover() => this.ChildrenOfType().SingleOrDefault(); } [Test] From 2fa52de87a07a7bbdbbde850ccd47d5722d1cc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 15:52:57 +0700 Subject: [PATCH 024/581] Fix formatting --- .../Editor/TestScenePreciseRotation.cs | 8 ++++---- osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs | 3 ++- .../Edit/Components/RadioButtons/EditorRadioButton.cs | 3 --- .../Screens/Edit/Components/RadioButtons/RadioButton.cs | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs index 67283f40da..30e0dbbf2e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePreciseRotation.cs @@ -46,8 +46,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { var popover = getPopover(); var buttons = popover.ChildrenOfType(); - return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value is false) && - buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value is true); + return buttons.Any(btn => btn.Text == "Selection centre" && !btn.Enabled.Value) + && buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value); }); AddStep("press rotate hotkey", () => { @@ -73,8 +73,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { var popover = getPopover(); var buttons = popover.ChildrenOfType(); - return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value is true) && - buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value is true); + return buttons.Any(btn => btn.Text == "Selection centre" && btn.Enabled.Value) + && buttons.Any(btn => btn.Text == "Playfield centre" && btn.Enabled.Value); }); AddStep("press rotate hotkey", () => { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs index 0ce78e4f61..cd01fc9f4d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs @@ -19,7 +19,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSelectionRotationHandler : SelectionRotationHandler { - public BindableBool CanRotatePlayfieldOrigin { get; private set; } = new(); + public BindableBool CanRotatePlayfieldOrigin { get; private set; } = new BindableBool(); + [Resolved] private IEditorChangeHandler? changeHandler { get; set; } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index 601548fadd..9d1f87e1e0 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -33,9 +33,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons private Drawable icon = null!; - [Resolved] - private EditorBeatmap? editorBeatmap { get; set; } - public EditorRadioButton(RadioButton button) { Button = button; diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index 1b47c028ab..2d1416c9c6 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -20,6 +20,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// Tooltip text that will be shown on hover if button is enabled. /// public LocalisableString TooltipTextWhenEnabled { get; set; } = string.Empty; + /// /// Tooltip text that will be shown on hover if button is disabled. /// From d5b70ed09a68c8c99484df97dd4fbd245c233e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Thu, 25 Jan 2024 16:56:59 +0700 Subject: [PATCH 025/581] Move CanRotatePlayfieldOrigin bindable to generic rotation handler --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 5 +---- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs | 2 -- osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs | 3 ++- .../Edit/Compose/Components/SelectionRotationHandler.cs | 6 +++++- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index a0fb0b06c3..448cfaf84c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -101,10 +101,7 @@ namespace osu.Game.Rulesets.Osu.Edit RightToolbox.AddRange(new EditorToolboxGroup[] { - new TransformToolboxGroup - { - RotationHandler = (OsuSelectionRotationHandler)BlueprintContainer.SelectionHandler.RotationHandler, - }, + new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }, FreehandlSliderToolboxGroup } ); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 7e645bc670..cea2adc6e2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Osu.Edit if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; } - public override OsuSelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(); + public override SelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(); private void scaleSlider(Slider slider, Vector2 scale) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs index cd01fc9f4d..1998e02a5c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSelectionRotationHandler : SelectionRotationHandler { - public BindableBool CanRotatePlayfieldOrigin { get; private set; } = new BindableBool(); - [Resolved] private IEditorChangeHandler? changeHandler { get; set; } diff --git a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs index c70f35c6fb..19590e9b6e 100644 --- a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Components; +using osu.Game.Screens.Edit.Compose.Components; using osuTK; namespace osu.Game.Rulesets.Osu.Edit @@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit private EditorToolButton rotateButton = null!; - public OsuSelectionRotationHandler RotationHandler { get; init; } = null!; + public SelectionRotationHandler RotationHandler { get; init; } = null!; public TransformToolboxGroup() : base("transform") diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs index 749e1aab17..459e4b0c41 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionRotationHandler.cs @@ -14,10 +14,14 @@ namespace osu.Game.Screens.Edit.Compose.Components { /// /// Whether rotation anchored by the selection origin can currently be performed. - /// This is in constrast to rotation anchored by the entire field. /// public Bindable CanRotateSelectionOrigin { get; private set; } = new BindableBool(); + /// + /// Whether rotation anchored by the center of the playfield can currently be performed. + /// + public Bindable CanRotatePlayfieldOrigin { get; private set; } = new BindableBool(); + /// /// Performs a single, instant, atomic rotation operation. /// From 3f9c2b41f7080639d3f4d94097cfeb3238ee503a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jan 2024 21:28:08 +0900 Subject: [PATCH 026/581] Adjust `BeatSyncContainer`'s early animate offset based on source's rate --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index f911311a09..a14dfd4d64 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -91,7 +91,17 @@ namespace osu.Game.Graphics.Containers if (IsBeatSyncedWithTrack) { - currentTrackTime = BeatSyncSource.Clock.CurrentTime + EarlyActivationMilliseconds; + double early = EarlyActivationMilliseconds; + + // In the case of gameplay, we are usually within a hierarchy with the correct rate applied to our `Drawable.Clock`. + // This means that the amount of early adjustment is adjusted in line with audio track rate changes. + // But other cases like the osu! logo at the main menu won't correctly have this rate information. + // + // So for cases where the rate of the source isn't in sync with our hierarchy, let's assume we need to account for it locally. + if (Clock.Rate == 1 && BeatSyncSource.Clock.Rate != Clock.Rate) + early *= BeatSyncSource.Clock.Rate; + + currentTrackTime = BeatSyncSource.Clock.CurrentTime + early; timingPoint = BeatSyncSource.ControlPoints?.TimingPointAt(currentTrackTime) ?? TimingControlPoint.DEFAULT; effectPoint = BeatSyncSource.ControlPoints?.EffectPointAt(currentTrackTime) ?? EffectControlPoint.DEFAULT; From a77db5d837bb41f57b533c7dff1b893eaa5148a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jan 2024 13:36:50 +0900 Subject: [PATCH 027/581] Add failing test coverage of editor metadata not saving --- .../Editing/TestSceneMetadataSection.cs | 105 ++++++++++++++++-- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs index a9f8e19e30..f767d9f7a3 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs @@ -3,17 +3,22 @@ #nullable disable +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Setup; +using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public partial class TestSceneMetadataSection : OsuTestScene + public partial class TestSceneMetadataSection : OsuManualInputManagerTestScene { [Cached] private EditorBeatmap editorBeatmap = new EditorBeatmap(new Beatmap @@ -26,6 +31,81 @@ namespace osu.Game.Tests.Visual.Editing private TestMetadataSection metadataSection; + [Test] + public void TestUpdateViaTextBoxOnFocusLoss() + { + AddStep("set metadata", () => + { + editorBeatmap.Metadata.Artist = "Example Artist"; + editorBeatmap.Metadata.ArtistUnicode = string.Empty; + }); + + createSection(); + + TextBox textbox = null!; + + AddStep("focus first textbox", () => + { + textbox = metadataSection.ChildrenOfType().First(); + InputManager.MoveMouseTo(textbox); + InputManager.Click(MouseButton.Left); + }); + + AddStep("simulate changing textbox", () => + { + // Can't simulate text input but this should work. + InputManager.Keys(PlatformAction.SelectAll); + InputManager.Keys(PlatformAction.Copy); + InputManager.Keys(PlatformAction.Paste); + InputManager.Keys(PlatformAction.Paste); + }); + + assertArtistMetadata("Example Artist"); + + // It's important values are committed immediately on focus loss so the editor exit sequence detects them. + AddAssert("value immediately changed on focus loss", () => + { + InputManager.TriggerFocusContention(metadataSection); + return editorBeatmap.Metadata.Artist; + }, () => Is.EqualTo("Example ArtistExample Artist")); + } + + [Test] + public void TestUpdateViaTextBoxOnCommit() + { + AddStep("set metadata", () => + { + editorBeatmap.Metadata.Artist = "Example Artist"; + editorBeatmap.Metadata.ArtistUnicode = string.Empty; + }); + + createSection(); + + TextBox textbox = null!; + + AddStep("focus first textbox", () => + { + textbox = metadataSection.ChildrenOfType().First(); + InputManager.MoveMouseTo(textbox); + InputManager.Click(MouseButton.Left); + }); + + AddStep("simulate changing textbox", () => + { + // Can't simulate text input but this should work. + InputManager.Keys(PlatformAction.SelectAll); + InputManager.Keys(PlatformAction.Copy); + InputManager.Keys(PlatformAction.Paste); + InputManager.Keys(PlatformAction.Paste); + }); + + assertArtistMetadata("Example Artist"); + + AddStep("commit", () => InputManager.Key(Key.Enter)); + + assertArtistMetadata("Example ArtistExample Artist"); + } + [Test] public void TestMinimalMetadata() { @@ -40,7 +120,7 @@ namespace osu.Game.Tests.Visual.Editing createSection(); - assertArtist("Example Artist"); + assertArtistTextBox("Example Artist"); assertRomanisedArtist("Example Artist", false); assertTitle("Example Title"); @@ -61,7 +141,7 @@ namespace osu.Game.Tests.Visual.Editing createSection(); - assertArtist("*なみりん"); + assertArtistTextBox("*なみりん"); assertRomanisedArtist(string.Empty, true); assertTitle("コイシテイク・プラネット"); @@ -82,7 +162,7 @@ namespace osu.Game.Tests.Visual.Editing createSection(); - assertArtist("*なみりん"); + assertArtistTextBox("*なみりん"); assertRomanisedArtist("*namirin", true); assertTitle("コイシテイク・プラネット"); @@ -104,11 +184,11 @@ namespace osu.Game.Tests.Visual.Editing createSection(); AddStep("set romanised artist name", () => metadataSection.ArtistTextBox.Current.Value = "*namirin"); - assertArtist("*namirin"); + assertArtistTextBox("*namirin"); assertRomanisedArtist("*namirin", false); AddStep("set native artist name", () => metadataSection.ArtistTextBox.Current.Value = "*なみりん"); - assertArtist("*なみりん"); + assertArtistTextBox("*なみりん"); assertRomanisedArtist("*namirin", true); AddStep("set romanised title", () => metadataSection.TitleTextBox.Current.Value = "Hitokoto no kyori"); @@ -123,21 +203,24 @@ namespace osu.Game.Tests.Visual.Editing private void createSection() => AddStep("create metadata section", () => Child = metadataSection = new TestMetadataSection()); - private void assertArtist(string expected) - => AddAssert($"artist is {expected}", () => metadataSection.ArtistTextBox.Current.Value == expected); + private void assertArtistMetadata(string expected) + => AddAssert($"artist metadata is {expected}", () => editorBeatmap.Metadata.Artist, () => Is.EqualTo(expected)); + + private void assertArtistTextBox(string expected) + => AddAssert($"artist textbox is {expected}", () => metadataSection.ArtistTextBox.Current.Value, () => Is.EqualTo(expected)); private void assertRomanisedArtist(string expected, bool editable) { - AddAssert($"romanised artist is {expected}", () => metadataSection.RomanisedArtistTextBox.Current.Value == expected); + AddAssert($"romanised artist is {expected}", () => metadataSection.RomanisedArtistTextBox.Current.Value, () => Is.EqualTo(expected)); AddAssert($"romanised artist is {(editable ? "" : "not ")}editable", () => metadataSection.RomanisedArtistTextBox.ReadOnly == !editable); } private void assertTitle(string expected) - => AddAssert($"title is {expected}", () => metadataSection.TitleTextBox.Current.Value == expected); + => AddAssert($"title is {expected}", () => metadataSection.TitleTextBox.Current.Value, () => Is.EqualTo(expected)); private void assertRomanisedTitle(string expected, bool editable) { - AddAssert($"romanised title is {expected}", () => metadataSection.RomanisedTitleTextBox.Current.Value == expected); + AddAssert($"romanised title is {expected}", () => metadataSection.RomanisedTitleTextBox.Current.Value, () => Is.EqualTo(expected)); AddAssert($"romanised title is {(editable ? "" : "not ")}editable", () => metadataSection.RomanisedTitleTextBox.ReadOnly == !editable); } From e54502eef1bd0d99e3a3b5c29476b0addc7a5c13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jan 2024 13:59:28 +0900 Subject: [PATCH 028/581] Add full editor save test coverage --- .../TestSceneBeatmapEditorNavigation.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 9930349b1b..370c40222e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -17,6 +18,7 @@ using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.GameplayTest; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -27,6 +29,59 @@ namespace osu.Game.Tests.Visual.Navigation { public partial class TestSceneBeatmapEditorNavigation : OsuGameTestScene { + [Test] + public void TestChangeMetadataExitWhileTextboxFocusedPromptsSave() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("change to song setup", () => InputManager.Key(Key.F4)); + + TextBox textbox = null!; + + AddUntilStep("wait for metadata section", () => + { + var t = Game.ChildrenOfType().SingleOrDefault().ChildrenOfType().FirstOrDefault(); + + if (t == null) + return false; + + textbox = t; + return true; + }); + + AddStep("focus textbox", () => + { + InputManager.MoveMouseTo(textbox); + InputManager.Click(MouseButton.Left); + }); + + AddStep("simulate changing textbox", () => + { + // Can't simulate text input but this should work. + InputManager.Keys(PlatformAction.SelectAll); + InputManager.Keys(PlatformAction.Copy); + InputManager.Keys(PlatformAction.Paste); + InputManager.Keys(PlatformAction.Paste); + }); + + AddStep("exit", () => Game.ChildrenOfType().Single().Exit()); + + AddAssert("save dialog displayed", () => Game.ChildrenOfType().Single().CurrentDialog is PromptForSaveDialog); + } + [Test] public void TestEditorGameplayTestAlwaysUsesOriginalRuleset() { From 45f2980dc099ba8c421395dd4258cdf3df4a2e55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Jan 2024 13:23:02 +0900 Subject: [PATCH 029/581] Fix potential editor data loss if exiting while a textbox is focused --- osu.Game/Screens/Edit/Editor.cs | 6 ++++++ osu.Game/Screens/Edit/Setup/MetadataSection.cs | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c1f6c02301..bc376f6165 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -719,6 +719,12 @@ namespace osu.Game.Screens.Edit public override bool OnExiting(ScreenExitEvent e) { + // Before exiting, trigger a focus loss. + // + // This is important to ensure that if the user is still editing a textbox, it will commit + // (and potentially block the exit procedure for save). + GetContainingInputManager().TriggerFocusContention(this); + if (!ExitConfirmed) { // dialog overlay may not be available in visual tests. diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 752f590308..b51c03b796 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -53,9 +53,6 @@ namespace osu.Game.Screens.Edit.Setup sourceTextBox = createTextBox(BeatmapsetsStrings.ShowInfoSource, metadata.Source), tagsTextBox = createTextBox(BeatmapsetsStrings.ShowInfoTags, metadata.Tags) }; - - foreach (var item in Children.OfType()) - item.OnCommit += onCommit; } private TTextBox createTextBox(LocalisableString label, string initialValue) @@ -77,6 +74,10 @@ namespace osu.Game.Screens.Edit.Setup ArtistTextBox.Current.BindValueChanged(artist => transferIfRomanised(artist.NewValue, RomanisedArtistTextBox)); TitleTextBox.Current.BindValueChanged(title => transferIfRomanised(title.NewValue, RomanisedTitleTextBox)); + + foreach (var item in Children.OfType()) + item.OnCommit += onCommit; + updateReadOnlyState(); } @@ -86,7 +87,6 @@ namespace osu.Game.Screens.Edit.Setup target.Current.Value = value; updateReadOnlyState(); - Scheduler.AddOnce(updateMetadata); } private void updateReadOnlyState() @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Edit.Setup // for now, update on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Scheduler.AddOnce(updateMetadata); + updateMetadata(); } private void updateMetadata() From fe0433e6ecd1a69132fee41626562590c4b688ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 2 Feb 2024 13:24:59 +0100 Subject: [PATCH 030/581] Remove redundant default value assignments --- osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs index f767d9f7a3..5930c077a4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Editing createSection(); - TextBox textbox = null!; + TextBox textbox; AddStep("focus first textbox", () => { @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Editing createSection(); - TextBox textbox = null!; + TextBox textbox; AddStep("focus first textbox", () => { From dbd4397bef2419ea4d9b1b8fdc44068358d59159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 2 Feb 2024 13:32:16 +0100 Subject: [PATCH 031/581] Attempt to salvage test by using until step --- .../Visual/Navigation/TestSceneBeatmapEditorNavigation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 370c40222e..e3a8e575f8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("exit", () => Game.ChildrenOfType().Single().Exit()); - AddAssert("save dialog displayed", () => Game.ChildrenOfType().Single().CurrentDialog is PromptForSaveDialog); + AddUntilStep("save dialog displayed", () => Game.ChildrenOfType().SingleOrDefault()?.CurrentDialog is PromptForSaveDialog); } [Test] From 6402f23f0245ecac17f82f1a9e5f06b9bd7898b2 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Mon, 12 Feb 2024 21:00:15 +0200 Subject: [PATCH 032/581] Added Traceable support for pp --- .../Difficulty/OsuPerformanceCalculator.cs | 12 ++++++++++++ osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 1 + 2 files changed, 13 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index b31f4ff519..4771bce280 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -116,6 +116,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } + else if (score.Mods.Any(h => h is OsuModTraceable)) + { + // Default 2% increase and another is scaled by AR + aimValue *= 1.02 + 0.02 * (12.0 - attributes.ApproachRate); + } // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator. double estimateDifficultSliders = attributes.SliderCount * 0.15; @@ -167,6 +172,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } + else if (score.Mods.Any(h => h is OsuModTraceable)) + { + // More reward for speed because speed on Traceable is annoying + speedValue *= 1.04 + 0.06 * (12.0 - attributes.ApproachRate); + } // Calculate accuracy assuming the worst case scenario double relevantTotalDiff = totalHits - attributes.SpeedNoteCount; @@ -214,6 +224,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty accuracyValue *= 1.14; else if (score.Mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; + else if (score.Mods.Any(m => m is OsuModTraceable)) + accuracyValue *= 1.02 + 0.01 * (12.0 - attributes.ApproachRate); if (score.Mods.Any(m => m is OsuModFlashlight)) accuracyValue *= 1.02; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 9671f53bea..320c0a7040 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; + public override bool Ranked => UsesDefaultConfiguration; public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) }; From 56391550096a19486e06d98a20e1f5bb1421c090 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Mon, 12 Feb 2024 23:31:00 +0200 Subject: [PATCH 033/581] Update OsuModTraceable.cs --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 320c0a7040..9671f53bea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override bool Ranked => UsesDefaultConfiguration; public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) }; From 0113fce02f5888dd775402ed4c15550826a5e999 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 11:27:12 +0100 Subject: [PATCH 034/581] Add osu!taiko `Constant Speed` mod --- .../Mods/TaikoModConstantSpeed.cs | 31 +++++++++++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 + 2 files changed, 32 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs new file mode 100644 index 0000000000..28de360eee --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModConstantSpeed : Mod, IApplicableToBeatmap + { + public override string Name => "Constant Speed"; + public override string Acronym => "CS"; + public override double ScoreMultiplier => 0.8; + public override LocalisableString Description => "No more tricky speed changes!"; + public override IconUsage? Icon => FontAwesome.Solid.Equals; + public override ModType Type => ModType.Conversion; + + public void ApplyToBeatmap(IBeatmap beatmap) + { + var taikoBeatmap = (TaikoBeatmap)beatmap; + + foreach (var effectControlPoint in taikoBeatmap.ControlPointInfo.EffectPoints) + { + effectControlPoint.ScrollSpeed = 1; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 24b0ec5d57..b701d3c25a 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -150,6 +150,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModClassic(), new TaikoModSwap(), new TaikoModSingleTap(), + new TaikoModConstantSpeed(), }; case ModType.Automation: From 1cbc2f07ab039c54d3788063e5028ab34577937a Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 14:01:12 +0100 Subject: [PATCH 035/581] use more correct implementation --- .../Mods/TaikoModConstantSpeed.cs | 20 +++++++++---------- .../UI/DrawableTaikoRuleset.cs | 7 ++++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs index 28de360eee..4ecb94467e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs @@ -1,15 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Localisation; using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModConstantSpeed : Mod, IApplicableToBeatmap + public class TaikoModConstantSpeed : Mod, IApplicableToDrawableRuleset { public override string Name => "Constant Speed"; public override string Acronym => "CS"; @@ -18,14 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Mods public override IconUsage? Icon => FontAwesome.Solid.Equals; public override ModType Type => ModType.Conversion; - public void ApplyToBeatmap(IBeatmap beatmap) + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - var taikoBeatmap = (TaikoBeatmap)beatmap; - - foreach (var effectControlPoint in taikoBeatmap.ControlPointInfo.EffectPoints) - { - effectControlPoint.ScrollSpeed = 1; - } + var taikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + taikoRuleset.VisualisationMethod = ScrollVisualisationMethod.Constant; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 77b2b06c0e..a476634fb8 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -69,7 +69,12 @@ namespace osu.Game.Rulesets.Taiko.UI TimeRange.Value = ComputeTimeRange(); } - protected virtual double ComputeTimeRange() => PlayfieldAdjustmentContainer.ComputeTimeRange(); + protected virtual double ComputeTimeRange() + { + // Adjust when we're using constant algorithm to not be sluggish. + double multiplier = VisualisationMethod == ScrollVisualisationMethod.Overlapping ? 1 : 4; + return PlayfieldAdjustmentContainer.ComputeTimeRange() / multiplier; + } protected override void UpdateAfterChildren() { From 14b0c41937f39906ddaf86d4919baadf2d5c2174 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 14:22:56 +0100 Subject: [PATCH 036/581] remove unnecessary `ComputeTimeRange` override --- osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs b/osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs index 3c7a97c864..217bb8139c 100644 --- a/osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs +++ b/osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs @@ -26,12 +26,5 @@ namespace osu.Game.Rulesets.Taiko.Edit ShowSpeedChanges.BindValueChanged(showChanges => VisualisationMethod = showChanges.NewValue ? ScrollVisualisationMethod.Overlapping : ScrollVisualisationMethod.Constant, true); } - - protected override double ComputeTimeRange() - { - // Adjust when we're using constant algorithm to not be sluggish. - double multiplier = ShowSpeedChanges.Value ? 1 : 4; - return base.ComputeTimeRange() / multiplier; - } } } From 7762d2469b59c4a349d5a05119f0c7a007cd0c5e Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 14:24:26 +0100 Subject: [PATCH 037/581] exclude EZ/HR for now --- osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs index 4ecb94467e..117dc0ebd2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.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. +using System; +using System.Linq; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; @@ -19,6 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override LocalisableString Description => "No more tricky speed changes!"; public override IconUsage? Icon => FontAwesome.Solid.Equals; public override ModType Type => ModType.Conversion; + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModEasy), typeof(TaikoModHardRock) }).ToArray(); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { From 65c0b73dd5c0b8a74deeab5aa737fb5d90dce82f Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 17:55:49 +0100 Subject: [PATCH 038/581] mark `TaikoModConstantSpeed` as incompatible with EZ/HR --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 3 +++ osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 009f2854f8..59d0563f1f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.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. +using System; +using System.Linq; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -10,6 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModEasy : ModEasy { public override LocalisableString Description => @"Beats move slower, and less accuracy required!"; + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModConstantSpeed) }).ToArray(); /// /// Multiplier factor added to the scrolling speed. diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index ba41175461..fa948507c8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.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. +using System; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -8,6 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModHardRock : ModHardRock { + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModConstantSpeed) }).ToArray(); public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; /// From 46a1f5267f40f03b566c4feb713de3a08a135ee8 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 21:15:18 +0100 Subject: [PATCH 039/581] account for beatmap base scroll speed in constant visualisation method --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index a476634fb8..c88bbec9bc 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected virtual double ComputeTimeRange() { // Adjust when we're using constant algorithm to not be sluggish. - double multiplier = VisualisationMethod == ScrollVisualisationMethod.Overlapping ? 1 : 4; + double multiplier = VisualisationMethod == ScrollVisualisationMethod.Overlapping ? 1 : 4 * Beatmap.Difficulty.SliderMultiplier; return PlayfieldAdjustmentContainer.ComputeTimeRange() / multiplier; } From 4ea9519db839762a088b2ef15fc8f770b0c87905 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 23 Feb 2024 21:15:52 +0100 Subject: [PATCH 040/581] revert changes to `IncompatibleMods` --- osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs | 3 --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 3 --- osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 3 --- 3 files changed, 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs index 117dc0ebd2..4ecb94467e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; @@ -21,7 +19,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public override LocalisableString Description => "No more tricky speed changes!"; public override IconUsage? Icon => FontAwesome.Solid.Equals; public override ModType Type => ModType.Conversion; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModEasy), typeof(TaikoModHardRock) }).ToArray(); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 59d0563f1f..009f2854f8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -12,7 +10,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModEasy : ModEasy { public override LocalisableString Description => @"Beats move slower, and less accuracy required!"; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModConstantSpeed) }).ToArray(); /// /// Multiplier factor added to the scrolling speed. diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index fa948507c8..ba41175461 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -10,7 +8,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModHardRock : ModHardRock { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(TaikoModConstantSpeed) }).ToArray(); public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; /// From f49aa4d8157eb6368263f919c662dd0afdaca1d0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 28 Feb 2024 22:01:39 +0800 Subject: [PATCH 041/581] Parse points and segments for path string --- .../Objects/Legacy/ConvertHitObjectParser.cs | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index f9e32fe26f..30e4101a84 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -266,30 +266,49 @@ namespace osu.Game.Rulesets.Objects.Legacy // This code takes on the responsibility of handling explicit segments of the path ("X" & "Y" from above). Implicit segments are handled by calls to convertPoints(). string[] pointSplit = pointString.Split('|'); - var controlPoints = new List>(); - int startIndex = 0; - int endIndex = 0; - bool first = true; + Span points = stackalloc Vector2[pointSplit.Length]; + Span<(PathType Type, int StartIndex)> segments = stackalloc (PathType Type, int StartIndex)[pointSplit.Length]; + int pointsCount = 0; + int segmentsCount = 0; - while (++endIndex < pointSplit.Length) + foreach (string s in pointSplit) { - // Keep incrementing endIndex while it's not the start of a new segment (indicated by having an alpha character at position 0). - if (!char.IsLetter(pointSplit[endIndex][0])) - continue; - - // Multi-segmented sliders DON'T contain the end point as part of the current segment as it's assumed to be the start of the next segment. - // The start of the next segment is the index after the type descriptor. - string endPoint = endIndex < pointSplit.Length - 1 ? pointSplit[endIndex + 1] : null; - - controlPoints.AddRange(convertPoints(pointSplit.AsMemory().Slice(startIndex, endIndex - startIndex), endPoint, first, offset)); - startIndex = endIndex; - first = false; + if (char.IsLetter(s[0])) + { + // The start of a new segment(indicated by having an alpha character at position 0). + var pathType = convertPathType(s); + segments[segmentsCount++] = (pathType, pointsCount); + } + else + { + points[pointsCount++] = readPoint(s, offset); + } } - if (endIndex > startIndex) - controlPoints.AddRange(convertPoints(pointSplit.AsMemory().Slice(startIndex, endIndex - startIndex), null, first, offset)); + var controlPoints = new List(pointsCount); - return mergePointsLists(controlPoints); + for (int i = 0; i < segmentsCount; i++) + { + int startIndex = segments[i].StartIndex; + int endIndex = i < segmentsCount - 1 ? segments[i + 1].StartIndex : pointsCount; + Vector2? endPoint = i < segmentsCount - 1 ? points[endIndex] : null; + controlPoints.AddRange(convertPoints(segments[i].Type, points[startIndex..endIndex], endPoint)); + } + + return controlPoints.ToArray(); + + static Vector2 readPoint(string value, Vector2 startPos) + { + string[] vertexSplit = value.Split(':'); + + Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos; + return pos; + } + } + + private IEnumerable convertPoints(PathType type, ReadOnlySpan points, Vector2? endPoint) + { + throw new NotImplementedException(); } /// From 4bff54d35dbd60aab7cd8a255977fe95e5c3bde7 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 28 Feb 2024 22:37:14 +0800 Subject: [PATCH 042/581] Add ToString on PathControlPoint for debugging --- osu.Game/Rulesets/Objects/PathControlPoint.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index ae9fa08085..1f8e63b269 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -76,5 +76,9 @@ namespace osu.Game.Rulesets.Objects } public bool Equals(PathControlPoint other) => Position == other?.Position && Type == other.Type; + + public override string ToString() => type is null + ? $"Position={Position}" + : $"Position={Position}, Type={type}"; } } From fe34577ee2938c45d295800405a309ad3d55ac3e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 28 Feb 2024 22:42:08 +0800 Subject: [PATCH 043/581] Update parsing. --- .../Objects/Legacy/ConvertHitObjectParser.cs | 86 ++++++------------- 1 file changed, 24 insertions(+), 62 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 30e4101a84..2d4d2865e6 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -278,6 +278,10 @@ namespace osu.Game.Rulesets.Objects.Legacy // The start of a new segment(indicated by having an alpha character at position 0). var pathType = convertPathType(s); segments[segmentsCount++] = (pathType, pointsCount); + + // First segment is prepended by an extra zero point + if (pointsCount == 0) + points[pointsCount++] = Vector2.Zero; } else { @@ -306,47 +310,30 @@ namespace osu.Game.Rulesets.Objects.Legacy } } - private IEnumerable convertPoints(PathType type, ReadOnlySpan points, Vector2? endPoint) - { - throw new NotImplementedException(); - } - /// /// Converts a given point list into a set of path segments. /// + /// The path type of the point list. /// The point list. /// Any extra endpoint to consider as part of the points. This will NOT be returned. - /// Whether this is the first segment in the set. If true the first of the returned segments will contain a zero point. - /// The positional offset to apply to the control points. - /// The set of points contained by as one or more segments of the path, prepended by an extra zero point if is true. - private IEnumerable> convertPoints(ReadOnlyMemory points, string endPoint, bool first, Vector2 offset) + /// The set of points contained by as one or more segments of the path. + private IEnumerable convertPoints(PathType type, ReadOnlySpan points, Vector2? endPoint) { - PathType type = convertPathType(points.Span[0]); - - int readOffset = first ? 1 : 0; // First control point is zero for the first segment. - int readablePoints = points.Length - 1; // Total points readable from the base point span. - int endPointLength = endPoint != null ? 1 : 0; // Extra length if an endpoint is given that lies outside the base point span. - - var vertices = new PathControlPoint[readOffset + readablePoints + endPointLength]; - - // Fill any non-read points. - for (int i = 0; i < readOffset; i++) - vertices[i] = new PathControlPoint(); + var vertices = new PathControlPoint[points.Length]; + var result = new List(); // Parse into control points. - for (int i = 1; i < points.Length; i++) - readPoint(points.Span[i], offset, out vertices[readOffset + i - 1]); - - // If an endpoint is given, add it to the end. - if (endPoint != null) - readPoint(endPoint, offset, out vertices[^1]); + for (int i = 0; i < points.Length; i++) + vertices[i] = new PathControlPoint { Position = points[i] }; // Edge-case rules (to match stable). if (type == PathType.PERFECT_CURVE) { - if (vertices.Length != 3) + int endPointLength = endPoint is null ? 0 : 1; + + if (vertices.Length + endPointLength != 3) type = PathType.BEZIER; - else if (isLinear(vertices)) + else if (isLinear(stackalloc[] { points[0], points[1], endPoint ?? points[2] })) { // osu-stable special-cased colinear perfect curves to a linear path type = PathType.LINEAR; @@ -365,7 +352,7 @@ namespace osu.Game.Rulesets.Objects.Legacy int startIndex = 0; int endIndex = 0; - while (++endIndex < vertices.Length - endPointLength) + while (++endIndex < vertices.Length) { // Keep incrementing while an implicit segment doesn't need to be started. if (vertices[endIndex].Position != vertices[endIndex - 1].Position) @@ -378,50 +365,25 @@ namespace osu.Game.Rulesets.Objects.Legacy continue; // The last control point of each segment is not allowed to start a new implicit segment. - if (endIndex == vertices.Length - endPointLength - 1) + if (endIndex == vertices.Length - 1) continue; // Force a type on the last point, and return the current control point set as a segment. vertices[endIndex - 1].Type = type; - yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex); + for (int i = startIndex; i < endIndex; i++) + result.Add(vertices[i]); // Skip the current control point - as it's the same as the one that's just been returned. startIndex = endIndex + 1; } - if (endIndex > startIndex) - yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex); + for (int i = startIndex; i < endIndex; i++) + result.Add(vertices[i]); - static void readPoint(string value, Vector2 startPos, out PathControlPoint point) - { - string[] vertexSplit = value.Split(':'); + return result; - Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos; - point = new PathControlPoint { Position = pos }; - } - - static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Y - p[0].Position.Y) * (p[2].Position.X - p[0].Position.X) - - (p[1].Position.X - p[0].Position.X) * (p[2].Position.Y - p[0].Position.Y)); - } - - private PathControlPoint[] mergePointsLists(List> controlPointList) - { - int totalCount = 0; - - foreach (var arr in controlPointList) - totalCount += arr.Length; - - var mergedArray = new PathControlPoint[totalCount]; - var mergedArrayMemory = mergedArray.AsMemory(); - int copyIndex = 0; - - foreach (var arr in controlPointList) - { - arr.CopyTo(mergedArrayMemory.Slice(copyIndex)); - copyIndex += arr.Length; - } - - return mergedArray; + static bool isLinear(ReadOnlySpan p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) + - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); } /// From bcb91f348d746bd4470240c23c72ec812fc54113 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 28 Feb 2024 22:51:36 +0800 Subject: [PATCH 044/581] Use ArrayPool instead of stackalloc --- .../Objects/Legacy/ConvertHitObjectParser.cs | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 2d4d2865e6..a3ca719ff9 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -18,6 +18,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Skinning; using osu.Game.Utils; +using System.Buffers; namespace osu.Game.Rulesets.Objects.Legacy { @@ -266,41 +267,49 @@ namespace osu.Game.Rulesets.Objects.Legacy // This code takes on the responsibility of handling explicit segments of the path ("X" & "Y" from above). Implicit segments are handled by calls to convertPoints(). string[] pointSplit = pointString.Split('|'); - Span points = stackalloc Vector2[pointSplit.Length]; - Span<(PathType Type, int StartIndex)> segments = stackalloc (PathType Type, int StartIndex)[pointSplit.Length]; + var points = ArrayPool.Shared.Rent(pointSplit.Length); + var segments = ArrayPool<(PathType Type, int StartIndex)>.Shared.Rent(pointSplit.Length); int pointsCount = 0; int segmentsCount = 0; - - foreach (string s in pointSplit) + try { - if (char.IsLetter(s[0])) - { - // The start of a new segment(indicated by having an alpha character at position 0). - var pathType = convertPathType(s); - segments[segmentsCount++] = (pathType, pointsCount); - // First segment is prepended by an extra zero point - if (pointsCount == 0) - points[pointsCount++] = Vector2.Zero; - } - else + foreach (string s in pointSplit) { - points[pointsCount++] = readPoint(s, offset); + if (char.IsLetter(s[0])) + { + // The start of a new segment(indicated by having an alpha character at position 0). + var pathType = convertPathType(s); + segments[segmentsCount++] = (pathType, pointsCount); + + // First segment is prepended by an extra zero point + if (pointsCount == 0) + points[pointsCount++] = Vector2.Zero; + } + else + { + points[pointsCount++] = readPoint(s, offset); + } } + + var controlPoints = new List>(pointsCount); + + for (int i = 0; i < segmentsCount; i++) + { + int startIndex = segments[i].StartIndex; + int endIndex = i < segmentsCount - 1 ? segments[i + 1].StartIndex : pointsCount; + Vector2? endPoint = i < segmentsCount - 1 ? points[endIndex] : null; + controlPoints.AddRange(convertPoints(segments[i].Type, new ArraySegment(points, startIndex, endIndex - startIndex), endPoint)); + } + + return controlPoints.SelectMany(s => s).ToArray(); } - - var controlPoints = new List(pointsCount); - - for (int i = 0; i < segmentsCount; i++) + finally { - int startIndex = segments[i].StartIndex; - int endIndex = i < segmentsCount - 1 ? segments[i + 1].StartIndex : pointsCount; - Vector2? endPoint = i < segmentsCount - 1 ? points[endIndex] : null; - controlPoints.AddRange(convertPoints(segments[i].Type, points[startIndex..endIndex], endPoint)); + ArrayPool.Shared.Return(points); + ArrayPool<(PathType Type, int StartIndex)>.Shared.Return(segments); } - return controlPoints.ToArray(); - static Vector2 readPoint(string value, Vector2 startPos) { string[] vertexSplit = value.Split(':'); @@ -317,13 +326,12 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The point list. /// Any extra endpoint to consider as part of the points. This will NOT be returned. /// The set of points contained by as one or more segments of the path. - private IEnumerable convertPoints(PathType type, ReadOnlySpan points, Vector2? endPoint) + private IEnumerable> convertPoints(PathType type, ArraySegment points, Vector2? endPoint) { - var vertices = new PathControlPoint[points.Length]; - var result = new List(); + var vertices = new PathControlPoint[points.Count]; // Parse into control points. - for (int i = 0; i < points.Length; i++) + for (int i = 0; i < points.Count; i++) vertices[i] = new PathControlPoint { Position = points[i] }; // Edge-case rules (to match stable). @@ -333,7 +341,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (vertices.Length + endPointLength != 3) type = PathType.BEZIER; - else if (isLinear(stackalloc[] { points[0], points[1], endPoint ?? points[2] })) + else if (isLinear(points[0], points[1], endPoint ?? points[2])) { // osu-stable special-cased colinear perfect curves to a linear path type = PathType.LINEAR; @@ -370,20 +378,18 @@ namespace osu.Game.Rulesets.Objects.Legacy // Force a type on the last point, and return the current control point set as a segment. vertices[endIndex - 1].Type = type; - for (int i = startIndex; i < endIndex; i++) - result.Add(vertices[i]); + yield return new ArraySegment(vertices, startIndex, endIndex - startIndex); // Skip the current control point - as it's the same as the one that's just been returned. startIndex = endIndex + 1; } - for (int i = startIndex; i < endIndex; i++) - result.Add(vertices[i]); + if (startIndex < endIndex) + yield return new ArraySegment(vertices, startIndex, endIndex - startIndex); - return result; - - static bool isLinear(ReadOnlySpan p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + static bool isLinear(Vector2 p0, Vector2 p1, Vector2 p2) + => Precision.AlmostEquals(0, (p1.Y - p0.Y) * (p2.X - p0.X) + - (p1.X - p0.X) * (p2.Y - p0.Y)); } /// From 470d2be2e1b4043953e067ef91e4c997138cc0ef Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 29 Feb 2024 00:07:00 +0800 Subject: [PATCH 045/581] The overhead of LINQ is not ignorable --- .../Objects/Legacy/ConvertHitObjectParser.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index a3ca719ff9..72053648a0 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Objects.Legacy controlPoints.AddRange(convertPoints(segments[i].Type, new ArraySegment(points, startIndex, endIndex - startIndex), endPoint)); } - return controlPoints.SelectMany(s => s).ToArray(); + return mergePointsLists(controlPoints); } finally { @@ -392,6 +392,25 @@ namespace osu.Game.Rulesets.Objects.Legacy - (p1.X - p0.X) * (p2.Y - p0.Y)); } + private PathControlPoint[] mergePointsLists(List> controlPointList) + { + int totalCount = 0; + + foreach (var arr in controlPointList) + totalCount += arr.Count; + + var mergedArray = new PathControlPoint[totalCount]; + int copyIndex = 0; + + foreach (var arr in controlPointList) + { + arr.AsSpan().CopyTo(mergedArray.AsSpan(copyIndex)); + copyIndex += arr.Count; + } + + return mergedArray; + } + /// /// Creates a legacy Hit-type hit object. /// From e86ebd6cdba85e506a771aeab0fdeb0a71d74fab Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 29 Feb 2024 00:24:24 +0800 Subject: [PATCH 046/581] Fix formatting --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 72053648a0..f042e6ba26 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -271,9 +271,9 @@ namespace osu.Game.Rulesets.Objects.Legacy var segments = ArrayPool<(PathType Type, int StartIndex)>.Shared.Rent(pointSplit.Length); int pointsCount = 0; int segmentsCount = 0; + try { - foreach (string s in pointSplit) { if (char.IsLetter(s[0])) From f28f19ed7eeee1ecacc909f0f4f35edb07c7b8cb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 29 Feb 2024 10:47:16 +0800 Subject: [PATCH 047/581] Fix method indent size Co-authored-by: Salman Ahmed --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index f042e6ba26..db1fbe3fa4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -389,7 +389,7 @@ namespace osu.Game.Rulesets.Objects.Legacy static bool isLinear(Vector2 p0, Vector2 p1, Vector2 p2) => Precision.AlmostEquals(0, (p1.Y - p0.Y) * (p2.X - p0.X) - - (p1.X - p0.X) * (p2.Y - p0.Y)); + - (p1.X - p0.X) * (p2.Y - p0.Y)); } private PathControlPoint[] mergePointsLists(List> controlPointList) From a11e63b184acc5030e3d167f13d85a3691f0b7dc Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 29 Feb 2024 20:02:04 +0800 Subject: [PATCH 048/581] Make the code more clear --- .../Objects/Legacy/ConvertHitObjectParser.cs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index db1fbe3fa4..2b058d5e1f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -265,49 +265,59 @@ namespace osu.Game.Rulesets.Objects.Legacy private PathControlPoint[] convertPathString(string pointString, Vector2 offset) { // This code takes on the responsibility of handling explicit segments of the path ("X" & "Y" from above). Implicit segments are handled by calls to convertPoints(). - string[] pointSplit = pointString.Split('|'); + string[] pointStringSplit = pointString.Split('|'); - var points = ArrayPool.Shared.Rent(pointSplit.Length); - var segments = ArrayPool<(PathType Type, int StartIndex)>.Shared.Rent(pointSplit.Length); - int pointsCount = 0; - int segmentsCount = 0; + var pointsBuffer = ArrayPool.Shared.Rent(pointStringSplit.Length); + var segmentsBuffer = ArrayPool<(PathType Type, int StartIndex)>.Shared.Rent(pointStringSplit.Length); + int currentPointsIndex = 0; + int currentSegmentsIndex = 0; try { - foreach (string s in pointSplit) + foreach (string s in pointStringSplit) { if (char.IsLetter(s[0])) { // The start of a new segment(indicated by having an alpha character at position 0). var pathType = convertPathType(s); - segments[segmentsCount++] = (pathType, pointsCount); + segmentsBuffer[currentSegmentsIndex++] = (pathType, currentPointsIndex); // First segment is prepended by an extra zero point - if (pointsCount == 0) - points[pointsCount++] = Vector2.Zero; + if (currentPointsIndex == 0) + pointsBuffer[currentPointsIndex++] = Vector2.Zero; } else { - points[pointsCount++] = readPoint(s, offset); + pointsBuffer[currentPointsIndex++] = readPoint(s, offset); } } + int pointsCount = currentPointsIndex; + int segmentsCount = currentSegmentsIndex; var controlPoints = new List>(pointsCount); + var allPoints = new ArraySegment(pointsBuffer, 0, pointsCount); for (int i = 0; i < segmentsCount; i++) { - int startIndex = segments[i].StartIndex; - int endIndex = i < segmentsCount - 1 ? segments[i + 1].StartIndex : pointsCount; - Vector2? endPoint = i < segmentsCount - 1 ? points[endIndex] : null; - controlPoints.AddRange(convertPoints(segments[i].Type, new ArraySegment(points, startIndex, endIndex - startIndex), endPoint)); + if (i < segmentsCount - 1) + { + int startIndex = segmentsBuffer[i].StartIndex; + int endIndex = segmentsBuffer[i + 1].StartIndex; + controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints[startIndex..endIndex], pointsBuffer[endIndex])); + } + else + { + int startIndex = segmentsBuffer[i].StartIndex; + controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints[startIndex..], null)); + } } return mergePointsLists(controlPoints); } finally { - ArrayPool.Shared.Return(points); - ArrayPool<(PathType Type, int StartIndex)>.Shared.Return(segments); + ArrayPool.Shared.Return(pointsBuffer); + ArrayPool<(PathType, int)>.Shared.Return(segmentsBuffer); } static Vector2 readPoint(string value, Vector2 startPos) From c05007804fb715b5d542535bcb224d5bd67b8b58 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Mar 2024 20:46:58 +0300 Subject: [PATCH 049/581] Use more direct way to apply transforms --- .../Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Storyboards/CommandTimeline.cs | 10 ++- osu.Game/Storyboards/CommandTimelineGroup.cs | 23 +++--- osu.Game/Storyboards/StoryboardSprite.cs | 77 +++++++++++++------ 4 files changed, 75 insertions(+), 37 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index cf4700bf85..a9a4d9cc49 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -212,7 +212,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, startValue, endValue); + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); break; } diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 0650c97165..2bb606d2bb 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -24,6 +24,13 @@ namespace osu.Game.Storyboards public T StartValue { get; private set; } public T EndValue { get; private set; } + public string PropertyName { get; } + + public CommandTimeline(string propertyName) + { + PropertyName = propertyName; + } + public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue) { if (endTime < startTime) @@ -31,7 +38,7 @@ namespace osu.Game.Storyboards endTime = startTime; } - commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue }); + commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, PropertyName = PropertyName }); if (startTime < StartTime) { @@ -55,6 +62,7 @@ namespace osu.Game.Storyboards public double StartTime { get; set; } public double EndTime { get; set; } public double Duration => EndTime - StartTime; + public string PropertyName { get; set; } public T StartValue; public T EndValue; diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index 0b96db6861..cb795e0ffe 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -3,11 +3,11 @@ using System; using osuTK; -using osuTK.Graphics; using osu.Framework.Graphics; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Graphics.Colour; namespace osu.Game.Storyboards { @@ -15,16 +15,16 @@ namespace osu.Game.Storyboards public class CommandTimelineGroup { - public CommandTimeline X = new CommandTimeline(); - public CommandTimeline Y = new CommandTimeline(); - public CommandTimeline Scale = new CommandTimeline(); - public CommandTimeline VectorScale = new CommandTimeline(); - public CommandTimeline Rotation = new CommandTimeline(); - public CommandTimeline Colour = new CommandTimeline(); - public CommandTimeline Alpha = new CommandTimeline(); - public CommandTimeline BlendingParameters = new CommandTimeline(); - public CommandTimeline FlipH = new CommandTimeline(); - public CommandTimeline FlipV = new CommandTimeline(); + public CommandTimeline X = new CommandTimeline("X"); + public CommandTimeline Y = new CommandTimeline("Y"); + public CommandTimeline Scale = new CommandTimeline("Scale"); + public CommandTimeline VectorScale = new CommandTimeline("VectorScale"); + public CommandTimeline Rotation = new CommandTimeline("Rotation"); + public CommandTimeline Colour = new CommandTimeline("Colour"); + public CommandTimeline Alpha = new CommandTimeline("Alpha"); + public CommandTimeline BlendingParameters = new CommandTimeline("Blending"); + public CommandTimeline FlipH = new CommandTimeline("FlipH"); + public CommandTimeline FlipV = new CommandTimeline("FlipV"); private readonly ICommandTimeline[] timelines; @@ -109,6 +109,7 @@ namespace osu.Game.Storyboards EndTime = offset + command.EndTime, StartValue = command.StartValue, EndValue = command.EndValue, + PropertyName = command.PropertyName }); } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 982185d51b..4d3f1c158f 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -98,8 +98,6 @@ namespace osu.Game.Storyboards private delegate void DrawablePropertyInitializer(Drawable drawable, T value); - private delegate void DrawableTransformer(Drawable drawable, T value, double duration, Easing easing); - public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition) { Path = path; @@ -132,27 +130,23 @@ namespace osu.Game.Storyboards List generated = new List(); - generateCommands(generated, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing)); - generateCommands(generated, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing)); - generateCommands(generated, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = new Vector2(value), (d, value, duration, easing) => d.ScaleTo(value, duration, easing)); - generateCommands(generated, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing)); - generateCommands(generated, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing)); - generateCommands(generated, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing)); - generateCommands(generated, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, _) => d.TransformBlendingMode(value, duration), - false); + generateCommands(generated, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value); + generateCommands(generated, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value); + generateCommands(generated, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value); + generateCommands(generated, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value); + generateCommands(generated, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value); + generateCommands(generated, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value); + generateCommands(generated, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, false); if (drawable is IVectorScalable vectorScalable) { - generateCommands(generated, getCommands(g => g.VectorScale, triggeredGroups), (_, value) => vectorScalable.VectorScale = value, - (_, value, duration, easing) => vectorScalable.VectorScaleTo(value, duration, easing)); + generateCommands(generated, getCommands(g => g.VectorScale, triggeredGroups), (_, value) => vectorScalable.VectorScale = value); } if (drawable is IFlippable flippable) { - generateCommands(generated, getCommands(g => g.FlipH, triggeredGroups), (_, value) => flippable.FlipH = value, (_, value, duration, _) => flippable.TransformFlipH(value, duration), - false); - generateCommands(generated, getCommands(g => g.FlipV, triggeredGroups), (_, value) => flippable.FlipV = value, (_, value, duration, _) => flippable.TransformFlipV(value, duration), - false); + generateCommands(generated, getCommands(g => g.FlipH, triggeredGroups), (_, value) => flippable.FlipH = value, false); + generateCommands(generated, getCommands(g => g.FlipV, triggeredGroups), (_, value) => flippable.FlipV = value, false); } foreach (var command in generated.OrderBy(g => g.StartTime)) @@ -160,7 +154,7 @@ namespace osu.Game.Storyboards } private void generateCommands(List resultList, IEnumerable.TypedCommand> commands, - DrawablePropertyInitializer initializeProperty, DrawableTransformer transform, bool alwaysInitialize = true) + DrawablePropertyInitializer initializeProperty, bool alwaysInitialize = true) { bool initialized = false; @@ -175,7 +169,7 @@ namespace osu.Game.Storyboards initialized = true; } - resultList.Add(new GeneratedCommand(command, initFunc, transform)); + resultList.Add(new GeneratedCommand(command, initFunc)); } } @@ -209,24 +203,59 @@ namespace osu.Game.Storyboards public double StartTime => command.StartTime; private readonly DrawablePropertyInitializer? initializeProperty; - private readonly DrawableTransformer transform; private readonly CommandTimeline.TypedCommand command; - public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty, DrawableTransformer transform) + public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty) { this.command = command; this.initializeProperty = initializeProperty; - this.transform = transform; } public void ApplyTo(Drawable drawable) { initializeProperty?.Invoke(drawable, command.StartValue); - using (drawable.BeginAbsoluteSequence(command.StartTime)) + switch (command.PropertyName) { - transform(drawable, command.StartValue, 0, Easing.None); - transform(drawable, command.EndValue, command.Duration, command.Easing); + case "VectorScale": + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + } + + break; + + case "FlipH": + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); + } + + break; + + case "FlipV": + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); + } + + break; + + case "Blending": + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); + } + + break; + + default: + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + } + + break; } } } From 7193ec66a4dee4abe516bd8984d3ed50559df35d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Mar 2024 21:30:46 +0300 Subject: [PATCH 050/581] Make use of framework's transform loops --- osu.Game/Storyboards/CommandLoop.cs | 18 ++++-- osu.Game/Storyboards/CommandTimeline.cs | 2 + osu.Game/Storyboards/StoryboardSprite.cs | 75 +++++++++++++++++++----- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs index 480d69c12f..a912daea44 100644 --- a/osu.Game/Storyboards/CommandLoop.cs +++ b/osu.Game/Storyboards/CommandLoop.cs @@ -38,11 +38,21 @@ namespace osu.Game.Storyboards public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) { - for (int loop = 0; loop < TotalIterations; loop++) + double fullLoopDuration = CommandsEndTime - CommandsStartTime; + + foreach (var command in timelineSelector(this).Commands) { - double loopOffset = LoopStartTime + loop * CommandsDuration; - foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset)) - yield return command; + yield return new CommandTimeline.TypedCommand + { + Easing = command.Easing, + StartTime = offset + LoopStartTime + command.StartTime, + EndTime = offset + LoopStartTime + command.EndTime, + StartValue = command.StartValue, + EndValue = command.EndValue, + PropertyName = command.PropertyName, + LoopCount = TotalIterations, + Delay = fullLoopDuration - command.EndTime + command.StartTime + }; } } diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 2bb606d2bb..ce25bfe25b 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -63,6 +63,8 @@ namespace osu.Game.Storyboards public double EndTime { get; set; } public double Duration => EndTime - StartTime; public string PropertyName { get; set; } + public int LoopCount { get; set; } + public double Delay { get; set; } public T StartValue; public T EndValue; diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 4d3f1c158f..2c04e4c983 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -218,41 +218,86 @@ namespace osu.Game.Storyboards switch (command.PropertyName) { case "VectorScale": - using (drawable.BeginAbsoluteSequence(command.StartTime)) + if (command.LoopCount == 0) { - ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + } + } + else + { + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) + .Loop(command.Delay, command.LoopCount); + } } break; case "FlipH": - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); - } - - break; - case "FlipV": - using (drawable.BeginAbsoluteSequence(command.StartTime)) + if (command.LoopCount == 0) { - ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue); + } + } + else + { + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue) + .Loop(command.Delay, command.LoopCount); + } } break; case "Blending": - using (drawable.BeginAbsoluteSequence(command.StartTime)) + if (command.LoopCount == 0) { - drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue); + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue); + } + } + else + { + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue) + .Loop(command.Delay, command.LoopCount); + } } break; default: - using (drawable.BeginAbsoluteSequence(command.StartTime)) + if (command.LoopCount == 0) { - drawable.TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + } + } + else + { + using (drawable.BeginAbsoluteSequence(command.StartTime)) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) + .Loop(command.Delay, command.LoopCount); + } } break; From f5d24e6804e64ffa720adc57116323cd682107e3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Mar 2024 21:54:37 +0300 Subject: [PATCH 051/581] Remove unused transform extensions --- .../Drawables/DrawablesExtensions.cs | 30 ------------- osu.Game/Storyboards/Drawables/IFlippable.cs | 42 ------------------- .../Storyboards/Drawables/IVectorScalable.cs | 8 ---- 3 files changed, 80 deletions(-) delete mode 100644 osu.Game/Storyboards/Drawables/DrawablesExtensions.cs diff --git a/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs b/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs deleted file mode 100644 index bbc55a336d..0000000000 --- a/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Transforms; - -namespace osu.Game.Storyboards.Drawables -{ - public static class DrawablesExtensions - { - /// - /// Adjusts after a delay. - /// - /// A to which further transforms can be added. - public static TransformSequence TransformBlendingMode(this T drawable, BlendingParameters newValue, double delay = 0) - where T : Drawable - => drawable.TransformTo(drawable.PopulateTransform(new TransformBlendingParameters(), newValue, delay)); - } - - public class TransformBlendingParameters : Transform - { - private BlendingParameters valueAt(double time) - => time < EndTime ? StartValue : EndValue; - - public override string TargetMember => nameof(Drawable.Blending); - - protected override void Apply(Drawable d, double time) => d.Blending = valueAt(time); - protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending; - } -} diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs index 165b3d97cc..2a931875ea 100644 --- a/osu.Game/Storyboards/Drawables/IFlippable.cs +++ b/osu.Game/Storyboards/Drawables/IFlippable.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Storyboards.Drawables @@ -11,45 +10,4 @@ namespace osu.Game.Storyboards.Drawables bool FlipH { get; set; } bool FlipV { get; set; } } - - internal class TransformFlipH : Transform - { - private bool valueAt(double time) - => time < EndTime ? StartValue : EndValue; - - public override string TargetMember => nameof(IFlippable.FlipH); - - protected override void Apply(IFlippable d, double time) => d.FlipH = valueAt(time); - protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH; - } - - internal class TransformFlipV : Transform - { - private bool valueAt(double time) - => time < EndTime ? StartValue : EndValue; - - public override string TargetMember => nameof(IFlippable.FlipV); - - protected override void Apply(IFlippable d, double time) => d.FlipV = valueAt(time); - protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV; - } - - internal static class FlippableExtensions - { - /// - /// Adjusts after a delay. - /// - /// A to which further transforms can be added. - public static TransformSequence TransformFlipH(this T flippable, bool newValue, double delay = 0) - where T : class, IFlippable - => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay)); - - /// - /// Adjusts after a delay. - /// - /// A to which further transforms can be added. - public static TransformSequence TransformFlipV(this T flippable, bool newValue, double delay = 0) - where T : class, IFlippable - => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay)); - } } diff --git a/osu.Game/Storyboards/Drawables/IVectorScalable.cs b/osu.Game/Storyboards/Drawables/IVectorScalable.cs index 60a297e126..ab0452df80 100644 --- a/osu.Game/Storyboards/Drawables/IVectorScalable.cs +++ b/osu.Game/Storyboards/Drawables/IVectorScalable.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; using osuTK; @@ -11,11 +10,4 @@ namespace osu.Game.Storyboards.Drawables { Vector2 VectorScale { get; set; } } - - internal static class VectorScalableExtensions - { - public static TransformSequence VectorScaleTo(this T target, Vector2 newVectorScale, double duration = 0, Easing easing = Easing.None) - where T : class, IVectorScalable - => target.TransformTo(nameof(IVectorScalable.VectorScale), newVectorScale, duration, easing); - } } From 4a7635e488169bf09dbcdfc7bf4da489a82d2b1c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Mar 2024 23:04:06 +0300 Subject: [PATCH 052/581] Fix broken tests --- osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index dadf3ca65f..dae3119ea4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -73,9 +73,9 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); // these should be ignored as we have an alpha visibility blocker proceeding this command. - sprite.TimelineGroup.Scale.Add(Easing.None, loop_start_time, -18000, 0, 1); + sprite.TimelineGroup.Scale.Add(Easing.None, loop_start_time, -18000, Vector2.Zero, Vector2.One); var loopGroup = sprite.AddLoop(loop_start_time, 50); - loopGroup.Scale.Add(Easing.None, loop_start_time, -18000, 0, 1); + loopGroup.Scale.Add(Easing.None, loop_start_time, -18000, Vector2.Zero, Vector2.One); var target = addEventToLoop ? loopGroup : sprite.TimelineGroup; double loopRelativeOffset = addEventToLoop ? -loop_start_time : 0; From 8b03acd27bdbdd6a997dbd70a00a7b4568e6b8bc Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Mar 2024 00:05:56 +0300 Subject: [PATCH 053/581] Implement StoryboardElementWithDuration --- .../Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Storyboards/CommandLoop.cs | 1 + osu.Game/Storyboards/CommandTimeline.cs | 4 +- osu.Game/Storyboards/CommandTimelineGroup.cs | 9 +- .../Drawables/DrawableStoryboardAnimation.cs | 2 +- .../Drawables/DrawableStoryboardSprite.cs | 2 +- ...pable.cs => IDrawableStoryboardElement.cs} | 6 +- .../Storyboards/Drawables/IVectorScalable.cs | 13 - osu.Game/Storyboards/StoryboardAnimation.cs | 5 +- .../StoryboardElementWithDuration.cs | 261 +++++++++++++++ osu.Game/Storyboards/StoryboardSprite.cs | 297 +----------------- 11 files changed, 284 insertions(+), 318 deletions(-) rename osu.Game/Storyboards/Drawables/{IFlippable.cs => IDrawableStoryboardElement.cs} (54%) delete mode 100644 osu.Game/Storyboards/Drawables/IVectorScalable.cs create mode 100644 osu.Game/Storyboards/StoryboardElementWithDuration.cs diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a9a4d9cc49..ba328b2dbd 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { - private StoryboardSprite? storyboardSprite; + private StoryboardElementWithDuration? storyboardSprite; private CommandTimelineGroup? timelineGroup; private Storyboard storyboard = null!; diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs index a912daea44..6dd782cb7f 100644 --- a/osu.Game/Storyboards/CommandLoop.cs +++ b/osu.Game/Storyboards/CommandLoop.cs @@ -50,6 +50,7 @@ namespace osu.Game.Storyboards StartValue = command.StartValue, EndValue = command.EndValue, PropertyName = command.PropertyName, + IsParameterCommand = command.IsParameterCommand, LoopCount = TotalIterations, Delay = fullLoopDuration - command.EndTime + command.StartTime }; diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index ce25bfe25b..4ad31d88c2 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -25,6 +25,7 @@ namespace osu.Game.Storyboards public T EndValue { get; private set; } public string PropertyName { get; } + public bool IsParameterTimeline { get; set; } public CommandTimeline(string propertyName) { @@ -38,7 +39,7 @@ namespace osu.Game.Storyboards endTime = startTime; } - commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, PropertyName = PropertyName }); + commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, PropertyName = PropertyName, IsParameterCommand = IsParameterTimeline }); if (startTime < StartTime) { @@ -65,6 +66,7 @@ namespace osu.Game.Storyboards public string PropertyName { get; set; } public int LoopCount { get; set; } public double Delay { get; set; } + public bool IsParameterCommand { get; set; } public T StartValue; public T EndValue; diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index cb795e0ffe..0362925619 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -22,9 +22,9 @@ namespace osu.Game.Storyboards public CommandTimeline Rotation = new CommandTimeline("Rotation"); public CommandTimeline Colour = new CommandTimeline("Colour"); public CommandTimeline Alpha = new CommandTimeline("Alpha"); - public CommandTimeline BlendingParameters = new CommandTimeline("Blending"); - public CommandTimeline FlipH = new CommandTimeline("FlipH"); - public CommandTimeline FlipV = new CommandTimeline("FlipV"); + public CommandTimeline BlendingParameters = new CommandTimeline("Blending") { IsParameterTimeline = true }; + public CommandTimeline FlipH = new CommandTimeline("FlipH") { IsParameterTimeline = true }; + public CommandTimeline FlipV = new CommandTimeline("FlipV") { IsParameterTimeline = true }; private readonly ICommandTimeline[] timelines; @@ -109,7 +109,8 @@ namespace osu.Game.Storyboards EndTime = offset + command.EndTime, StartValue = command.StartValue, EndValue = command.EndValue, - PropertyName = command.PropertyName + PropertyName = command.PropertyName, + IsParameterCommand = command.IsParameterCommand }); } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index fae9ec7f2e..8e1a8ce949 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Storyboards.Drawables { - public partial class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable + public partial class DrawableStoryboardAnimation : TextureAnimation, IDrawableStoryboardElement { public StoryboardAnimation Animation { get; } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ec875219b6..6772178e85 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Storyboards.Drawables { - public partial class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable + public partial class DrawableStoryboardSprite : Sprite, IDrawableStoryboardElement { public StoryboardSprite Sprite { get; } diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs similarity index 54% rename from osu.Game/Storyboards/Drawables/IFlippable.cs rename to osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs index 2a931875ea..6652b5509c 100644 --- a/osu.Game/Storyboards/Drawables/IFlippable.cs +++ b/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs @@ -1,13 +1,15 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Transforms; +using osuTK; namespace osu.Game.Storyboards.Drawables { - internal interface IFlippable : ITransformable + public interface IDrawableStoryboardElement : ITransformable { bool FlipH { get; set; } bool FlipV { get; set; } + Vector2 VectorScale { get; set; } } } diff --git a/osu.Game/Storyboards/Drawables/IVectorScalable.cs b/osu.Game/Storyboards/Drawables/IVectorScalable.cs deleted file mode 100644 index ab0452df80..0000000000 --- a/osu.Game/Storyboards/Drawables/IVectorScalable.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Transforms; -using osuTK; - -namespace osu.Game.Storyboards.Drawables -{ - internal interface IVectorScalable : ITransformable - { - Vector2 VectorScale { get; set; } - } -} diff --git a/osu.Game/Storyboards/StoryboardAnimation.cs b/osu.Game/Storyboards/StoryboardAnimation.cs index 1a4b6bb923..173acf7ff1 100644 --- a/osu.Game/Storyboards/StoryboardAnimation.cs +++ b/osu.Game/Storyboards/StoryboardAnimation.cs @@ -7,7 +7,7 @@ using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { - public class StoryboardAnimation : StoryboardSprite + public class StoryboardAnimation : StoryboardElementWithDuration { public int FrameCount; public double FrameDelay; @@ -21,8 +21,7 @@ namespace osu.Game.Storyboards LoopType = loopType; } - public override Drawable CreateDrawable() - => new DrawableStoryboardAnimation(this); + public override DrawableStoryboardAnimation CreateStoryboardDrawable() => new DrawableStoryboardAnimation(this); } public enum AnimationLoopType diff --git a/osu.Game/Storyboards/StoryboardElementWithDuration.cs b/osu.Game/Storyboards/StoryboardElementWithDuration.cs new file mode 100644 index 0000000000..0c89b40c36 --- /dev/null +++ b/osu.Game/Storyboards/StoryboardElementWithDuration.cs @@ -0,0 +1,261 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Storyboards.Drawables; +using osuTK; + +namespace osu.Game.Storyboards +{ + public abstract class StoryboardElementWithDuration : IStoryboardElementWithDuration + { + protected readonly List Loops = new List(); + private readonly List triggers = new List(); + + public string Path { get; } + public bool IsDrawable => HasCommands; + + public Anchor Origin; + public Vector2 InitialPosition; + + public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); + + public double StartTime + { + get + { + // To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a StartValue of zero. + // A StartValue of zero governs, above all else, the first valid display time of a sprite. + // + // You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero, + // anything before that point can be ignored (the sprite is not visible after all). + var alphaCommands = new List<(double startTime, bool isZeroStartValue)>(); + + var command = TimelineGroup.Alpha.Commands.FirstOrDefault(); + if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); + + foreach (var loop in Loops) + { + command = loop.Alpha.Commands.FirstOrDefault(); + if (command != null) alphaCommands.Add((command.StartTime + loop.LoopStartTime, command.StartValue == 0)); + } + + if (alphaCommands.Count > 0) + { + var firstAlpha = alphaCommands.MinBy(t => t.startTime); + + if (firstAlpha.isZeroStartValue) + return firstAlpha.startTime; + } + + return EarliestTransformTime; + } + } + + public double EarliestTransformTime + { + get + { + // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. + // The sprite's StartTime will be determined by the earliest command, regardless of type. + double earliestStartTime = TimelineGroup.StartTime; + foreach (var l in Loops) + earliestStartTime = Math.Min(earliestStartTime, l.StartTime); + return earliestStartTime; + } + } + + public double EndTime + { + get + { + double latestEndTime = TimelineGroup.EndTime; + + foreach (var l in Loops) + latestEndTime = Math.Max(latestEndTime, l.EndTime); + + return latestEndTime; + } + } + + public double EndTimeForDisplay + { + get + { + double latestEndTime = TimelineGroup.EndTime; + + foreach (var l in Loops) + latestEndTime = Math.Max(latestEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); + + return latestEndTime; + } + } + + public bool HasCommands => TimelineGroup.HasCommands || Loops.Any(l => l.HasCommands); + + protected StoryboardElementWithDuration(string path, Anchor origin, Vector2 initialPosition) + { + Path = path; + Origin = origin; + InitialPosition = initialPosition; + } + + public abstract Drawable CreateDrawable(); + + public CommandLoop AddLoop(double startTime, int repeatCount) + { + var loop = new CommandLoop(startTime, repeatCount); + Loops.Add(loop); + return loop; + } + + public CommandTrigger AddTrigger(string triggerName, double startTime, double endTime, int groupNumber) + { + var trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber); + triggers.Add(trigger); + return trigger; + } + + protected IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, IEnumerable>? triggeredGroups) + { + var commands = TimelineGroup.GetCommands(timelineSelector); + foreach (var loop in Loops) + commands = commands.Concat(loop.GetCommands(timelineSelector)); + + if (triggeredGroups != null) + { + foreach (var pair in triggeredGroups) + commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2)); + } + + return commands; + } + + public override string ToString() + => $"{Path}, {Origin}, {InitialPosition}"; + } + + public abstract class StoryboardElementWithDuration : StoryboardElementWithDuration + where U : Drawable, IDrawableStoryboardElement + { + private delegate void DrawablePropertyInitializer(U drawable, T value); + + protected StoryboardElementWithDuration(string path, Anchor origin, Vector2 initialPosition) + : base(path, origin, initialPosition) + { + } + + public override Drawable CreateDrawable() => CreateStoryboardDrawable(); + + public abstract U CreateStoryboardDrawable(); + + public void ApplyTransforms(U drawable, IEnumerable>? triggeredGroups = null) + { + // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. + // To achieve this, commands are "generated" as pairs of (command, initFunc, transformFunc) and batched into a contiguous list + // The list is then stably-sorted (to preserve command order), and applied to the drawable sequentially. + + List> generated = new List>(); + + generateCommands(generated, GetCommands(g => g.X, triggeredGroups), (d, value) => d.X = value); + generateCommands(generated, GetCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value); + generateCommands(generated, GetCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value); + generateCommands(generated, GetCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value); + generateCommands(generated, GetCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value); + generateCommands(generated, GetCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value); + generateCommands(generated, GetCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, false); + generateCommands(generated, GetCommands(g => g.VectorScale, triggeredGroups), (d, value) => d.VectorScale = value); + generateCommands(generated, GetCommands(g => g.FlipH, triggeredGroups), (d, value) => d.FlipH = value, false); + generateCommands(generated, GetCommands(g => g.FlipV, triggeredGroups), (d, value) => d.FlipV = value, false); + + foreach (var command in generated.OrderBy(g => g.StartTime)) + command.ApplyTo(drawable); + } + + private void generateCommands(List> resultList, IEnumerable.TypedCommand> commands, + DrawablePropertyInitializer initializeProperty, bool alwaysInitialize = true) + { + bool initialized = false; + + foreach (var command in commands) + { + DrawablePropertyInitializer? initFunc = null; + + if (!initialized) + { + if (alwaysInitialize || command.StartTime == command.EndTime) + initFunc = initializeProperty; + initialized = true; + } + + resultList.Add(new GeneratedCommand(command, initFunc)); + } + } + + private interface IGeneratedCommand + where TDrawable : U + { + double StartTime { get; } + + void ApplyTo(TDrawable drawable); + } + + private readonly struct GeneratedCommand : IGeneratedCommand + where TDrawable : U + { + public double StartTime => command.StartTime; + + private readonly DrawablePropertyInitializer? initializeProperty; + private readonly CommandTimeline.TypedCommand command; + + public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty) + { + this.command = command; + this.initializeProperty = initializeProperty; + } + + public void ApplyTo(TDrawable drawable) + { + initializeProperty?.Invoke(drawable, command.StartValue); + + using (drawable.BeginAbsoluteSequence(command.StartTime)) + transform(drawable); + } + + private void transform(TDrawable drawable) + { + if (command.IsParameterCommand) + { + if (command.LoopCount == 0) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue); + } + else + { + drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) + .TransformTo(command.PropertyName, command.EndValue) + .Loop(command.Delay, command.LoopCount); + } + } + else + { + if (command.LoopCount == 0) + { + drawable.TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); + } + else + { + drawable.TransformTo(command.PropertyName, command.StartValue).Then() + .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) + .Loop(command.Delay, command.LoopCount); + } + } + } + } + } +} diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 2c04e4c983..69994f77a4 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -1,308 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Graphics; using osu.Game.Storyboards.Drawables; using osuTK; namespace osu.Game.Storyboards { - public class StoryboardSprite : IStoryboardElementWithDuration + public class StoryboardSprite : StoryboardElementWithDuration { - private readonly List loops = new List(); - private readonly List triggers = new List(); - - public string Path { get; } - public bool IsDrawable => HasCommands; - - public Anchor Origin; - public Vector2 InitialPosition; - - public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); - - public double StartTime - { - get - { - // To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a StartValue of zero. - // A StartValue of zero governs, above all else, the first valid display time of a sprite. - // - // You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero, - // anything before that point can be ignored (the sprite is not visible after all). - var alphaCommands = new List<(double startTime, bool isZeroStartValue)>(); - - var command = TimelineGroup.Alpha.Commands.FirstOrDefault(); - if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); - - foreach (var loop in loops) - { - command = loop.Alpha.Commands.FirstOrDefault(); - if (command != null) alphaCommands.Add((command.StartTime + loop.LoopStartTime, command.StartValue == 0)); - } - - if (alphaCommands.Count > 0) - { - var firstAlpha = alphaCommands.MinBy(t => t.startTime); - - if (firstAlpha.isZeroStartValue) - return firstAlpha.startTime; - } - - return EarliestTransformTime; - } - } - - public double EarliestTransformTime - { - get - { - // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. - // The sprite's StartTime will be determined by the earliest command, regardless of type. - double earliestStartTime = TimelineGroup.StartTime; - foreach (var l in loops) - earliestStartTime = Math.Min(earliestStartTime, l.StartTime); - return earliestStartTime; - } - } - - public double EndTime - { - get - { - double latestEndTime = TimelineGroup.EndTime; - - foreach (var l in loops) - latestEndTime = Math.Max(latestEndTime, l.EndTime); - - return latestEndTime; - } - } - - public double EndTimeForDisplay - { - get - { - double latestEndTime = TimelineGroup.EndTime; - - foreach (var l in loops) - latestEndTime = Math.Max(latestEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); - - return latestEndTime; - } - } - - public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); - - private delegate void DrawablePropertyInitializer(Drawable drawable, T value); - public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition) + : base(path, origin, initialPosition) { - Path = path; - Origin = origin; - InitialPosition = initialPosition; } - public CommandLoop AddLoop(double startTime, int repeatCount) - { - var loop = new CommandLoop(startTime, repeatCount); - loops.Add(loop); - return loop; - } - - public CommandTrigger AddTrigger(string triggerName, double startTime, double endTime, int groupNumber) - { - var trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber); - triggers.Add(trigger); - return trigger; - } - - public virtual Drawable CreateDrawable() - => new DrawableStoryboardSprite(this); - - public void ApplyTransforms(Drawable drawable, IEnumerable>? triggeredGroups = null) - { - // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. - // To achieve this, commands are "generated" as pairs of (command, initFunc, transformFunc) and batched into a contiguous list - // The list is then stably-sorted (to preserve command order), and applied to the drawable sequentially. - - List generated = new List(); - - generateCommands(generated, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value); - generateCommands(generated, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value); - generateCommands(generated, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value); - generateCommands(generated, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value); - generateCommands(generated, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value); - generateCommands(generated, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value); - generateCommands(generated, getCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, false); - - if (drawable is IVectorScalable vectorScalable) - { - generateCommands(generated, getCommands(g => g.VectorScale, triggeredGroups), (_, value) => vectorScalable.VectorScale = value); - } - - if (drawable is IFlippable flippable) - { - generateCommands(generated, getCommands(g => g.FlipH, triggeredGroups), (_, value) => flippable.FlipH = value, false); - generateCommands(generated, getCommands(g => g.FlipV, triggeredGroups), (_, value) => flippable.FlipV = value, false); - } - - foreach (var command in generated.OrderBy(g => g.StartTime)) - command.ApplyTo(drawable); - } - - private void generateCommands(List resultList, IEnumerable.TypedCommand> commands, - DrawablePropertyInitializer initializeProperty, bool alwaysInitialize = true) - { - bool initialized = false; - - foreach (var command in commands) - { - DrawablePropertyInitializer? initFunc = null; - - if (!initialized) - { - if (alwaysInitialize || command.StartTime == command.EndTime) - initFunc = initializeProperty; - initialized = true; - } - - resultList.Add(new GeneratedCommand(command, initFunc)); - } - } - - private IEnumerable.TypedCommand> getCommands(CommandTimelineSelector timelineSelector, IEnumerable>? triggeredGroups) - { - var commands = TimelineGroup.GetCommands(timelineSelector); - foreach (var loop in loops) - commands = commands.Concat(loop.GetCommands(timelineSelector)); - - if (triggeredGroups != null) - { - foreach (var pair in triggeredGroups) - commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2)); - } - - return commands; - } - - public override string ToString() - => $"{Path}, {Origin}, {InitialPosition}"; - - private interface IGeneratedCommand - { - double StartTime { get; } - - void ApplyTo(Drawable drawable); - } - - private readonly struct GeneratedCommand : IGeneratedCommand - { - public double StartTime => command.StartTime; - - private readonly DrawablePropertyInitializer? initializeProperty; - private readonly CommandTimeline.TypedCommand command; - - public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty) - { - this.command = command; - this.initializeProperty = initializeProperty; - } - - public void ApplyTo(Drawable drawable) - { - initializeProperty?.Invoke(drawable, command.StartValue); - - switch (command.PropertyName) - { - case "VectorScale": - if (command.LoopCount == 0) - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); - } - } - else - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - ((IVectorScalable)drawable).TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) - .Loop(command.Delay, command.LoopCount); - } - } - - break; - - case "FlipH": - case "FlipV": - if (command.LoopCount == 0) - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue); - } - } - else - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - ((IFlippable)drawable).TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue) - .Loop(command.Delay, command.LoopCount); - } - } - - break; - - case "Blending": - if (command.LoopCount == 0) - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue); - } - } - else - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue) - .Loop(command.Delay, command.LoopCount); - } - } - - break; - - default: - if (command.LoopCount == 0) - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); - } - } - else - { - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) - .Loop(command.Delay, command.LoopCount); - } - } - - break; - } - } - } + public override DrawableStoryboardSprite CreateStoryboardDrawable() => new DrawableStoryboardSprite(this); } } + + From 1c8ede854d5ba97deafeea83abee96c61cb367bb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Mar 2024 00:17:56 +0300 Subject: [PATCH 054/581] Remove blank lines --- osu.Game/Storyboards/StoryboardSprite.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 69994f77a4..8eaab9428d 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -17,5 +17,3 @@ namespace osu.Game.Storyboards public override DrawableStoryboardSprite CreateStoryboardDrawable() => new DrawableStoryboardSprite(this); } } - - From 07392a4d3eb7ff9c08c863d0aaf27cb33161dac7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Mar 2024 01:10:22 +0300 Subject: [PATCH 055/581] Further simplify transform application --- .../StoryboardElementWithDuration.cs | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/osu.Game/Storyboards/StoryboardElementWithDuration.cs b/osu.Game/Storyboards/StoryboardElementWithDuration.cs index 0c89b40c36..06924a26ef 100644 --- a/osu.Game/Storyboards/StoryboardElementWithDuration.cs +++ b/osu.Game/Storyboards/StoryboardElementWithDuration.cs @@ -222,38 +222,13 @@ namespace osu.Game.Storyboards initializeProperty?.Invoke(drawable, command.StartValue); using (drawable.BeginAbsoluteSequence(command.StartTime)) - transform(drawable); - } + { + var sequence = command.IsParameterCommand + ? drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue) + : drawable.TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); - private void transform(TDrawable drawable) - { - if (command.IsParameterCommand) - { - if (command.LoopCount == 0) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue); - } - else - { - drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration) - .TransformTo(command.PropertyName, command.EndValue) - .Loop(command.Delay, command.LoopCount); - } - } - else - { - if (command.LoopCount == 0) - { - drawable.TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); - } - else - { - drawable.TransformTo(command.PropertyName, command.StartValue).Then() - .TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing) - .Loop(command.Delay, command.LoopCount); - } + if (command.LoopCount > 0) + sequence.Loop(command.Delay, command.LoopCount); } } } From 585ab5976877281be70cb6f52721382c5a86a861 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 6 Mar 2024 19:39:28 +0300 Subject: [PATCH 056/581] Apply major refactor to the storyboard commands flow structrure --- .../TestSceneDrawableStoryboardSprite.cs | 3 +- .../Visual/Gameplay/TestSceneLeadIn.cs | 16 +- .../Gameplay/TestSceneStoryboardWithOutro.cs | 2 +- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Formats/LegacyStoryboardDecoder.cs | 39 +-- osu.Game/Storyboards/CommandLoop.cs | 63 ----- osu.Game/Storyboards/CommandTimeline.cs | 101 -------- osu.Game/Storyboards/CommandTimelineGroup.cs | 120 --------- .../Commands/IStoryboardCommand.cs | 28 +++ .../Commands/StoryboardAlphaCommand.cs | 19 ++ .../StoryboardBlendingParametersCommand.cs | 21 ++ .../Commands/StoryboardColourCommand.cs | 20 ++ .../Storyboards/Commands/StoryboardCommand.cs | 54 ++++ .../Commands/StoryboardCommandGroup.cs | 115 +++++++++ .../Commands/StoryboardCommandList.cs | 41 +++ .../Commands/StoryboardFlipHCommand.cs | 23 ++ .../Commands/StoryboardFlipVCommand.cs | 23 ++ .../Commands/StoryboardLoopingGroup.cs | 71 ++++++ .../Commands/StoryboardRotationCommand.cs | 21 ++ .../Commands/StoryboardScaleCommand.cs | 22 ++ .../StoryboardTriggerGroup.cs} | 7 +- .../Commands/StoryboardVectorScaleCommand.cs | 24 ++ .../Commands/StoryboardXCommand.cs | 21 ++ .../Commands/StoryboardYCommand.cs | 21 ++ .../Drawables/IDrawableStoryboardElement.cs | 4 +- osu.Game/Storyboards/StoryboardAnimation.cs | 4 +- .../StoryboardElementWithDuration.cs | 236 ------------------ osu.Game/Storyboards/StoryboardSprite.cs | 153 +++++++++++- 28 files changed, 713 insertions(+), 561 deletions(-) delete mode 100644 osu.Game/Storyboards/CommandLoop.cs delete mode 100644 osu.Game/Storyboards/CommandTimeline.cs delete mode 100644 osu.Game/Storyboards/CommandTimelineGroup.cs create mode 100644 osu.Game/Storyboards/Commands/IStoryboardCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardColourCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardCommandList.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs rename osu.Game/Storyboards/{CommandTrigger.cs => Commands/StoryboardTriggerGroup.cs} (68%) create mode 100644 osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardXCommand.cs create mode 100644 osu.Game/Storyboards/Commands/StoryboardYCommand.cs delete mode 100644 osu.Game/Storyboards/StoryboardElementWithDuration.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 32693c2bb2..6209b42cbb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -175,7 +175,8 @@ namespace osu.Game.Tests.Visual.Gameplay var layer = storyboard.GetLayer("Background"); var sprite = new StoryboardSprite(lookupName, origin, initialPosition); - sprite.AddLoop(Time.Current, 100).Alpha.Add(Easing.None, 0, 10000, 1, 1); + var loop = sprite.AddLoopingGroup(Time.Current, 100); + loop.AddAlpha(0, 10000, 1, 1, Easing.None); layer.Elements.Clear(); layer.Add(sprite); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index dae3119ea4..c3eef4da9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1); + sprite.Group.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None); storyboard.GetLayer("Background").Add(sprite); @@ -73,17 +73,17 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); // these should be ignored as we have an alpha visibility blocker proceeding this command. - sprite.TimelineGroup.Scale.Add(Easing.None, loop_start_time, -18000, Vector2.Zero, Vector2.One); - var loopGroup = sprite.AddLoop(loop_start_time, 50); - loopGroup.Scale.Add(Easing.None, loop_start_time, -18000, Vector2.Zero, Vector2.One); + sprite.Group.AddScale(loop_start_time, -18000, 0, 1, Easing.None); + var loopGroup = sprite.AddLoopingGroup(loop_start_time, 50); + loopGroup.AddScale(loop_start_time, -18000, 0, 1, Easing.None); - var target = addEventToLoop ? loopGroup : sprite.TimelineGroup; + var target = addEventToLoop ? loopGroup : sprite.Group; double loopRelativeOffset = addEventToLoop ? -loop_start_time : 0; - target.Alpha.Add(Easing.None, loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1); + target.AddAlpha(loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1, Easing.None); // these should be ignored due to being in the future. - sprite.TimelineGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1); - loopGroup.Alpha.Add(Easing.None, 38000, 40000, 0, 1); + sprite.Group.AddAlpha(18000, 20000, 0, 1, Easing.None); + loopGroup.AddAlpha(38000, 40000, 0, 1, Easing.None); storyboard.GetLayer("Background").Add(sprite); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index f532921d63..9269c3f4ae 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var storyboard = new Storyboard(); var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, 0, duration, 1, 0); + sprite.Group.AddAlpha(0, duration, 1, 0, Easing.None); storyboard.GetLayer("Background").Add(sprite); return storyboard; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index cebc75f90c..62a2bfeaab 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -424,7 +424,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestIntroStoryboardElement() => testLeadIn(b => { var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, -2000, 0, 0, 1); + sprite.Group.AddAlpha(-2000, 0, 0, 1, Easing.None); b.Storyboard.GetLayer("Background").Add(sprite); }); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index ba328b2dbd..33cdaa085e 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -10,6 +10,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps.Legacy; using osu.Game.IO; using osu.Game.Storyboards; +using osu.Game.Storyboards.Commands; using osuTK; using osuTK.Graphics; @@ -17,8 +18,8 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { - private StoryboardElementWithDuration? storyboardSprite; - private CommandTimelineGroup? timelineGroup; + private StoryboardSprite? storyboardSprite; + private StoryboardCommandGroup? currentGroup; private Storyboard storyboard = null!; @@ -165,7 +166,7 @@ namespace osu.Game.Beatmaps.Formats else { if (depth < 2) - timelineGroup = storyboardSprite?.TimelineGroup; + currentGroup = storyboardSprite?.Group; string commandType = split[0]; @@ -177,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats double startTime = split.Length > 2 ? Parsing.ParseDouble(split[2]) : double.MinValue; double endTime = split.Length > 3 ? Parsing.ParseDouble(split[3]) : double.MaxValue; int groupNumber = split.Length > 4 ? Parsing.ParseInt(split[4]) : 0; - timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); + currentGroup = storyboardSprite?.AddTriggerGroup(triggerName, startTime, endTime, groupNumber); break; } @@ -185,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats { double startTime = Parsing.ParseDouble(split[1]); int repeatCount = Parsing.ParseInt(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount - 1)); + currentGroup = storyboardSprite?.AddLoopingGroup(startTime, Math.Max(0, repeatCount - 1)); break; } @@ -204,7 +205,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); + currentGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing); break; } @@ -212,7 +213,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + currentGroup?.AddScale(startTime, endTime, startValue, endValue, easing); break; } @@ -222,7 +223,7 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - timelineGroup?.VectorScale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + currentGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing); break; } @@ -230,7 +231,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.Rotation.Add(easing, startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue)); + currentGroup?.AddRotation(startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue), easing); break; } @@ -240,8 +241,8 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); - timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); + currentGroup?.AddX(startTime, endTime, startX, endX, easing); + currentGroup?.AddY(startTime, endTime, startY, endY, easing); break; } @@ -249,7 +250,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); + currentGroup?.AddX(startTime, endTime, startValue, endValue, easing); break; } @@ -257,7 +258,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); + currentGroup?.AddY(startTime, endTime, startValue, endValue, easing); break; } @@ -269,9 +270,9 @@ namespace osu.Game.Beatmaps.Formats float endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed; float endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen; float endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue; - timelineGroup?.Colour.Add(easing, startTime, endTime, + currentGroup?.AddColour(startTime, endTime, new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1), easing); break; } @@ -282,16 +283,16 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case "A": - timelineGroup?.BlendingParameters.Add(easing, startTime, endTime, BlendingParameters.Additive, - startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit); + currentGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive, + startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit, easing); break; case "H": - timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + currentGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing); break; case "V": - timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + currentGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing); break; } diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs deleted file mode 100644 index 6dd782cb7f..0000000000 --- a/osu.Game/Storyboards/CommandLoop.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; - -namespace osu.Game.Storyboards -{ - public class CommandLoop : CommandTimelineGroup - { - public double LoopStartTime; - - /// - /// The total number of times this loop is played back. Always greater than zero. - /// - public readonly int TotalIterations; - - public override double StartTime => LoopStartTime + CommandsStartTime; - - public override double EndTime => - // In an ideal world, we would multiply the command duration by TotalIterations here. - // Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro - // sequences for minutes or hours. - StartTime + CommandsDuration; - - /// - /// Construct a new command loop. - /// - /// The start time of the loop. - /// The number of times the loop should repeat. Should be greater than zero. Zero means a single playback. - public CommandLoop(double startTime, int repeatCount) - { - if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); - - LoopStartTime = startTime; - TotalIterations = repeatCount + 1; - } - - public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) - { - double fullLoopDuration = CommandsEndTime - CommandsStartTime; - - foreach (var command in timelineSelector(this).Commands) - { - yield return new CommandTimeline.TypedCommand - { - Easing = command.Easing, - StartTime = offset + LoopStartTime + command.StartTime, - EndTime = offset + LoopStartTime + command.EndTime, - StartValue = command.StartValue, - EndValue = command.EndValue, - PropertyName = command.PropertyName, - IsParameterCommand = command.IsParameterCommand, - LoopCount = TotalIterations, - Delay = fullLoopDuration - command.EndTime + command.StartTime - }; - } - } - - public override string ToString() - => $"{LoopStartTime} x{TotalIterations}"; - } -} diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs deleted file mode 100644 index 4ad31d88c2..0000000000 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using osu.Framework.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Storyboards -{ - public class CommandTimeline : ICommandTimeline - { - private readonly List commands = new List(); - - public IEnumerable Commands => commands.OrderBy(c => c.StartTime); - - public bool HasCommands => commands.Count > 0; - - public double StartTime { get; private set; } = double.MaxValue; - public double EndTime { get; private set; } = double.MinValue; - - public T StartValue { get; private set; } - public T EndValue { get; private set; } - - public string PropertyName { get; } - public bool IsParameterTimeline { get; set; } - - public CommandTimeline(string propertyName) - { - PropertyName = propertyName; - } - - public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue) - { - if (endTime < startTime) - { - endTime = startTime; - } - - commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, PropertyName = PropertyName, IsParameterCommand = IsParameterTimeline }); - - if (startTime < StartTime) - { - StartValue = startValue; - StartTime = startTime; - } - - if (endTime > EndTime) - { - EndValue = endValue; - EndTime = endTime; - } - } - - public override string ToString() - => $"{commands.Count} command(s)"; - - public class TypedCommand : ICommand - { - public Easing Easing { get; set; } - public double StartTime { get; set; } - public double EndTime { get; set; } - public double Duration => EndTime - StartTime; - public string PropertyName { get; set; } - public int LoopCount { get; set; } - public double Delay { get; set; } - public bool IsParameterCommand { get; set; } - - public T StartValue; - public T EndValue; - - public int CompareTo(ICommand other) - { - int result = StartTime.CompareTo(other.StartTime); - if (result != 0) return result; - - return EndTime.CompareTo(other.EndTime); - } - - public override string ToString() - => $"{StartTime} -> {EndTime}, {StartValue} -> {EndValue} {Easing}"; - } - } - - public interface ICommandTimeline - { - double StartTime { get; } - double EndTime { get; } - bool HasCommands { get; } - } - - public interface ICommand : IComparable - { - Easing Easing { get; set; } - double StartTime { get; set; } - double EndTime { get; set; } - double Duration { get; } - } -} diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs deleted file mode 100644 index 0362925619..0000000000 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osuTK; -using osu.Framework.Graphics; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.Graphics.Colour; - -namespace osu.Game.Storyboards -{ - public delegate CommandTimeline CommandTimelineSelector(CommandTimelineGroup commandTimelineGroup); - - public class CommandTimelineGroup - { - public CommandTimeline X = new CommandTimeline("X"); - public CommandTimeline Y = new CommandTimeline("Y"); - public CommandTimeline Scale = new CommandTimeline("Scale"); - public CommandTimeline VectorScale = new CommandTimeline("VectorScale"); - public CommandTimeline Rotation = new CommandTimeline("Rotation"); - public CommandTimeline Colour = new CommandTimeline("Colour"); - public CommandTimeline Alpha = new CommandTimeline("Alpha"); - public CommandTimeline BlendingParameters = new CommandTimeline("Blending") { IsParameterTimeline = true }; - public CommandTimeline FlipH = new CommandTimeline("FlipH") { IsParameterTimeline = true }; - public CommandTimeline FlipV = new CommandTimeline("FlipV") { IsParameterTimeline = true }; - - private readonly ICommandTimeline[] timelines; - - public CommandTimelineGroup() - { - timelines = new ICommandTimeline[] - { - X, - Y, - Scale, - VectorScale, - Rotation, - Colour, - Alpha, - BlendingParameters, - FlipH, - FlipV - }; - } - - [JsonIgnore] - public double CommandsStartTime - { - get - { - double min = double.MaxValue; - - for (int i = 0; i < timelines.Length; i++) - min = Math.Min(min, timelines[i].StartTime); - - return min; - } - } - - [JsonIgnore] - public double CommandsEndTime - { - get - { - double max = double.MinValue; - - for (int i = 0; i < timelines.Length; i++) - max = Math.Max(max, timelines[i].EndTime); - - return max; - } - } - - [JsonIgnore] - public double CommandsDuration => CommandsEndTime - CommandsStartTime; - - [JsonIgnore] - public virtual double StartTime => CommandsStartTime; - - [JsonIgnore] - public virtual double EndTime => CommandsEndTime; - - [JsonIgnore] - public bool HasCommands - { - get - { - for (int i = 0; i < timelines.Length; i++) - { - if (timelines[i].HasCommands) - return true; - } - - return false; - } - } - - public virtual IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) - { - if (offset != 0) - { - return timelineSelector(this).Commands.Select(command => - new CommandTimeline.TypedCommand - { - Easing = command.Easing, - StartTime = offset + command.StartTime, - EndTime = offset + command.EndTime, - StartValue = command.StartValue, - EndValue = command.EndValue, - PropertyName = command.PropertyName, - IsParameterCommand = command.IsParameterCommand - }); - } - - return timelineSelector(this).Commands; - } - } -} diff --git a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs new file mode 100644 index 0000000000..848dcab575 --- /dev/null +++ b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public interface IStoryboardCommand + { + /// + /// The start time of the storyboard command. + /// + double StartTime { get; } + + /// + /// The end time of the storyboard command. + /// + double EndTime { get; } + + /// + /// Applies the transforms described by this storyboard command to the target drawable. + /// + /// The target drawable. + /// The sequence of transforms applied to the target drawable. + TransformSequence ApplyTransform(Drawable d); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs new file mode 100644 index 0000000000..729ecd72a7 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardAlphaCommand : StoryboardCommand + { + public StoryboardAlphaCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Alpha = StartValue; + public override TransformSequence ApplyTransform(Drawable d) => d.FadeTo(StartValue).Then().FadeTo(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs new file mode 100644 index 0000000000..cc54909837 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardBlendingParametersCommand : StoryboardCommand + { + public StoryboardBlendingParametersCommand(double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Blending = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration).TransformTo(nameof(d.Blending), EndValue); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs new file mode 100644 index 0000000000..be56a1d71b --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osuTK.Graphics; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardColourCommand : StoryboardCommand + { + public StoryboardColourCommand(double startTime, double endTime, Color4 startValue, Color4 endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Colour = StartValue; + public override TransformSequence ApplyTransform(Drawable d) => d.FadeColour(StartValue).Then().FadeColour(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardCommand.cs b/osu.Game/Storyboards/Commands/StoryboardCommand.cs new file mode 100644 index 0000000000..883b9f57c1 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardCommand.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public abstract class StoryboardCommand : IStoryboardCommand + { + public double StartTime { get; } + public double EndTime { get; } + public double Duration => EndTime - StartTime; + + protected StoryboardCommand(double startTime, double endTime, T startValue, T endValue, Easing easing) + { + if (endTime < startTime) + endTime = startTime; + + StartTime = startTime; + StartValue = startValue; + EndTime = endTime; + EndValue = endValue; + Easing = easing; + } + + public Easing Easing { get; set; } + public int LoopCount { get; set; } + public double Delay { get; set; } + + public T StartValue; + public T EndValue; + + /// + /// Sets the value of the corresponding property in to the start value of this command. + /// + public abstract void SetInitialValue(Drawable d); + + /// + /// Transforms a corresponding property in that corresponds to this command group with the specified parameters. + /// + public abstract TransformSequence ApplyTransform(Drawable d); + + public int CompareTo(IStoryboardCommand other) + { + int result = StartTime.CompareTo(other.StartTime); + if (result != 0) return result; + + return EndTime.CompareTo(other.EndTime); + } + + public override string ToString() => $"{StartTime} -> {EndTime}, {StartValue} -> {EndValue} {Easing}"; + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs new file mode 100644 index 0000000000..02c43c9f60 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -0,0 +1,115 @@ +// 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 System.Linq; +using Newtonsoft.Json; +using osu.Framework.Graphics; +using osu.Framework.Lists; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardCommandGroup + { + public SortedList> X; + public SortedList> Y; + public SortedList> Scale; + public SortedList> VectorScale; + public SortedList> Rotation; + public SortedList> Colour; + public SortedList> Alpha; + public SortedList> BlendingParameters; + public SortedList> FlipH; + public SortedList> FlipV; + + private readonly IReadOnlyList[] lists; + + /// + /// Returns the earliest start time of the commands added to this group. + /// + [JsonIgnore] + public double StartTime { get; private set; } + + /// + /// Returns the latest end time of the commands added to this group. + /// + [JsonIgnore] + public double EndTime { get; private set; } + + [JsonIgnore] + public double Duration => EndTime - StartTime; + + [JsonIgnore] + public bool HasCommands { get; private set; } + + public StoryboardCommandGroup() + { + lists = new IReadOnlyList[] + { + X = new SortedList>(), + Y = new SortedList>(), + Scale = new SortedList>(), + VectorScale = new SortedList>(), + Rotation = new SortedList>(), + Colour = new SortedList>(), + Alpha = new SortedList>(), + BlendingParameters = new SortedList>(), + FlipH = new SortedList>(), + FlipV = new SortedList>() + }; + } + + /// + /// Returns all commands contained by this group unsorted. + /// + public virtual IEnumerable GetAllCommands() => lists.SelectMany(l => l); + + public void AddX(double startTime, double endTime, float startValue, float endValue, Easing easing) + => AddCommand(X, new StoryboardXCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddY(double startTime, double endTime, float startValue, float endValue, Easing easing) + => AddCommand(Y, new StoryboardYCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddScale(double startTime, double endTime, float startValue, float endValue, Easing easing) + => AddCommand(Scale, new StoryboardScaleCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddVectorScale(double startTime, double endTime, Vector2 startValue, Vector2 endValue, Easing easing) + => AddCommand(VectorScale, new StoryboardVectorScaleCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddRotation(double startTime, double endTime, float startValue, float endValue, Easing easing) + => AddCommand(Rotation, new StoryboardRotationCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddColour(double startTime, double endTime, Color4 startValue, Color4 endValue, Easing easing) + => AddCommand(Colour, new StoryboardColourCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddAlpha(double startTime, double endTime, float startValue, float endValue, Easing easing) + => AddCommand(Alpha, new StoryboardAlphaCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddBlendingParameters(double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue, Easing easing) + => AddCommand(BlendingParameters, new StoryboardBlendingParametersCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddFlipH(double startTime, double endTime, bool startValue, bool endValue, Easing easing) + => AddCommand(FlipH, new StoryboardFlipHCommand(startTime, endTime, startValue, endValue, easing)); + + public void AddFlipV(double startTime, double endTime, bool startValue, bool endValue, Easing easing) + => AddCommand(FlipV, new StoryboardFlipVCommand(startTime, endTime, startValue, endValue, easing)); + + /// + /// Adds the given storyboard to the target . + /// Can be overriden to apply custom effects to the given command before adding it to the list (e.g. looping or time offsets). + /// + /// The value type of the target property affected by this storyboard command. + protected virtual void AddCommand(ICollection> list, StoryboardCommand command) + { + list.Add(command); + + if (command.StartTime < StartTime) + StartTime = command.StartTime; + + if (command.EndTime > EndTime) + EndTime = command.EndTime; + } + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandList.cs b/osu.Game/Storyboards/Commands/StoryboardCommandList.cs new file mode 100644 index 0000000000..67012e9d49 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardCommandList.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Storyboards.Commands +{ + // public class StoryboardCommandList : IStoryboardCommandList + // { + // // todo: change to sorted list and avoid enumerable type on exposed properties? + // private readonly List> commands = new List>(); + // + // public IEnumerable> Commands => commands.OrderBy(c => c.StartTime); + // + // IEnumerable IStoryboardCommandList.Commands => Commands; + // public bool HasCommands => commands.Count > 0; + // + // public double StartTime { get; private set; } = double.MaxValue; + // public double EndTime { get; private set; } = double.MinValue; + // + // public T? StartValue { get; private set; } + // public T? EndValue { get; private set; } + // + // public void Add(StoryboardCommand command) + // { + // commands.Add(command); + // + // if (command.StartTime < StartTime) + // { + // StartValue = command.StartValue; + // StartTime = command.StartTime; + // } + // + // if (command.EndTime > EndTime) + // { + // EndValue = command.EndValue; + // EndTime = command.EndTime; + // } + // } + // + // public override string ToString() => $"{commands.Count} command(s)"; + // } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs new file mode 100644 index 0000000000..9bcb687d3c --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osu.Game.Storyboards.Drawables; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardFlipHCommand : StoryboardCommand + { + public StoryboardFlipHCommand(double startTime, double endTime, bool startValue, bool endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipH = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.TransformTo(nameof(IDrawableStoryboardElement.FlipH), StartValue).Delay(Duration) + .TransformTo(nameof(IDrawableStoryboardElement.FlipH), EndValue); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs new file mode 100644 index 0000000000..9f1f5faa33 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osu.Game.Storyboards.Drawables; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardFlipVCommand : StoryboardCommand + { + public StoryboardFlipVCommand(double startTime, double endTime, bool startValue, bool endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipV = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.TransformTo(nameof(IDrawableStoryboardElement.FlipV), StartValue).Delay(Duration) + .TransformTo(nameof(IDrawableStoryboardElement.FlipV), EndValue); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs new file mode 100644 index 0000000000..e520353bd6 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardLoopingGroup : StoryboardCommandGroup + { + public double LoopStartTime; + + /// + /// The total number of times this loop is played back. Always greater than zero. + /// + public readonly int TotalIterations; + + /// + /// Construct a new command loop. + /// + /// The start time of the loop. + /// The number of times the loop should repeat. Should be greater than zero. Zero means a single playback. + public StoryboardLoopingGroup(double startTime, int repeatCount) + { + if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); + + LoopStartTime = startTime; + TotalIterations = repeatCount + 1; + } + + protected override void AddCommand(ICollection> list, StoryboardCommand command) + { + // todo: this is broke! + double fullLoopDuration = EndTime - StartTime; + double loopDelay = fullLoopDuration - command.EndTime + command.StartTime; + base.AddCommand(list, new StoryboardLoopingCommand(command, LoopStartTime, TotalIterations, loopDelay)); + } + + public override string ToString() => $"{LoopStartTime} x{TotalIterations}"; + + private class StoryboardLoopingCommand : StoryboardCommand + { + private readonly StoryboardCommand command; + private readonly int loopCount; + private readonly double loopDelay; + + public StoryboardLoopingCommand(StoryboardCommand command, double loopStartTime, int loopCount, double loopDelay) + // In an ideal world, we would multiply the command duration by TotalIterations in command end time. + // Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro + // sequences for minutes or hours. + : base(loopStartTime + command.StartTime, loopStartTime + command.EndTime, command.StartValue, command.EndValue, command.Easing) + { + this.command = command; + this.loopCount = loopCount; + this.loopDelay = loopDelay; + } + + public override void SetInitialValue(Drawable d) => command.SetInitialValue(d); + + public override TransformSequence ApplyTransform(Drawable d) + { + if (loopCount == 0) + return command.ApplyTransform(d); + + return command.ApplyTransform(d).Loop(loopDelay, loopCount); + } + } + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs new file mode 100644 index 0000000000..c56dcd130f --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardRotationCommand : StoryboardCommand + { + public StoryboardRotationCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Rotation = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.RotateTo(StartValue).Then().RotateTo(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs new file mode 100644 index 0000000000..9dbdd6ebd8 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osuTK; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardScaleCommand : StoryboardCommand + { + public StoryboardScaleCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Scale = new Vector2(StartValue); + + public override TransformSequence ApplyTransform(Drawable d) + => d.ScaleTo(StartValue).Then().ScaleTo(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/CommandTrigger.cs b/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs similarity index 68% rename from osu.Game/Storyboards/CommandTrigger.cs rename to osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs index 011f345df2..dfb6f8cb1b 100644 --- a/osu.Game/Storyboards/CommandTrigger.cs +++ b/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs @@ -1,16 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Storyboards +namespace osu.Game.Storyboards.Commands { - public class CommandTrigger : CommandTimelineGroup + // todo: this is not implemented and has never been, keep that in mind. + public class StoryboardTriggerGroup : StoryboardCommandGroup { public string TriggerName; public double TriggerStartTime; public double TriggerEndTime; public int GroupNumber; - public CommandTrigger(string triggerName, double startTime, double endTime, int groupNumber) + public StoryboardTriggerGroup(string triggerName, double startTime, double endTime, int groupNumber) { TriggerName = triggerName; TriggerStartTime = startTime; diff --git a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs new file mode 100644 index 0000000000..fefb21b257 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osu.Game.Storyboards.Drawables; +using osuTK; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardVectorScaleCommand : StoryboardCommand + { + public StoryboardVectorScaleCommand(double startTime, double endTime, Vector2 startValue, Vector2 endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).VectorScale = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.TransformTo(nameof(IDrawableStoryboardElement.VectorScale), StartValue).Then() + .TransformTo(nameof(IDrawableStoryboardElement.VectorScale), EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs new file mode 100644 index 0000000000..a9f9cd4f8f --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardXCommand : StoryboardCommand + { + public StoryboardXCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.X = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.MoveToX(StartValue).Then().MoveToX(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs new file mode 100644 index 0000000000..eb30b36720 --- /dev/null +++ b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Storyboards.Commands +{ + public class StoryboardYCommand : StoryboardCommand + { + public StoryboardYCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) + : base(startTime, endTime, startValue, endValue, easing) + { + } + + public override void SetInitialValue(Drawable d) => d.Y = StartValue; + + public override TransformSequence ApplyTransform(Drawable d) + => d.MoveToY(StartValue).Then().MoveToY(EndValue, Duration, Easing); + } +} diff --git a/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs b/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs index 6652b5509c..04bae88c76 100644 --- a/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs +++ b/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs @@ -1,12 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics.Transforms; +using osu.Framework.Graphics; using osuTK; namespace osu.Game.Storyboards.Drawables { - public interface IDrawableStoryboardElement : ITransformable + public interface IDrawableStoryboardElement : IDrawable { bool FlipH { get; set; } bool FlipV { get; set; } diff --git a/osu.Game/Storyboards/StoryboardAnimation.cs b/osu.Game/Storyboards/StoryboardAnimation.cs index 173acf7ff1..0b714633c9 100644 --- a/osu.Game/Storyboards/StoryboardAnimation.cs +++ b/osu.Game/Storyboards/StoryboardAnimation.cs @@ -7,7 +7,7 @@ using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards { - public class StoryboardAnimation : StoryboardElementWithDuration + public class StoryboardAnimation : StoryboardSprite { public int FrameCount; public double FrameDelay; @@ -21,7 +21,7 @@ namespace osu.Game.Storyboards LoopType = loopType; } - public override DrawableStoryboardAnimation CreateStoryboardDrawable() => new DrawableStoryboardAnimation(this); + public override Drawable CreateDrawable() => new DrawableStoryboardAnimation(this); } public enum AnimationLoopType diff --git a/osu.Game/Storyboards/StoryboardElementWithDuration.cs b/osu.Game/Storyboards/StoryboardElementWithDuration.cs deleted file mode 100644 index 06924a26ef..0000000000 --- a/osu.Game/Storyboards/StoryboardElementWithDuration.cs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Storyboards.Drawables; -using osuTK; - -namespace osu.Game.Storyboards -{ - public abstract class StoryboardElementWithDuration : IStoryboardElementWithDuration - { - protected readonly List Loops = new List(); - private readonly List triggers = new List(); - - public string Path { get; } - public bool IsDrawable => HasCommands; - - public Anchor Origin; - public Vector2 InitialPosition; - - public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); - - public double StartTime - { - get - { - // To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a StartValue of zero. - // A StartValue of zero governs, above all else, the first valid display time of a sprite. - // - // You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero, - // anything before that point can be ignored (the sprite is not visible after all). - var alphaCommands = new List<(double startTime, bool isZeroStartValue)>(); - - var command = TimelineGroup.Alpha.Commands.FirstOrDefault(); - if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); - - foreach (var loop in Loops) - { - command = loop.Alpha.Commands.FirstOrDefault(); - if (command != null) alphaCommands.Add((command.StartTime + loop.LoopStartTime, command.StartValue == 0)); - } - - if (alphaCommands.Count > 0) - { - var firstAlpha = alphaCommands.MinBy(t => t.startTime); - - if (firstAlpha.isZeroStartValue) - return firstAlpha.startTime; - } - - return EarliestTransformTime; - } - } - - public double EarliestTransformTime - { - get - { - // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. - // The sprite's StartTime will be determined by the earliest command, regardless of type. - double earliestStartTime = TimelineGroup.StartTime; - foreach (var l in Loops) - earliestStartTime = Math.Min(earliestStartTime, l.StartTime); - return earliestStartTime; - } - } - - public double EndTime - { - get - { - double latestEndTime = TimelineGroup.EndTime; - - foreach (var l in Loops) - latestEndTime = Math.Max(latestEndTime, l.EndTime); - - return latestEndTime; - } - } - - public double EndTimeForDisplay - { - get - { - double latestEndTime = TimelineGroup.EndTime; - - foreach (var l in Loops) - latestEndTime = Math.Max(latestEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); - - return latestEndTime; - } - } - - public bool HasCommands => TimelineGroup.HasCommands || Loops.Any(l => l.HasCommands); - - protected StoryboardElementWithDuration(string path, Anchor origin, Vector2 initialPosition) - { - Path = path; - Origin = origin; - InitialPosition = initialPosition; - } - - public abstract Drawable CreateDrawable(); - - public CommandLoop AddLoop(double startTime, int repeatCount) - { - var loop = new CommandLoop(startTime, repeatCount); - Loops.Add(loop); - return loop; - } - - public CommandTrigger AddTrigger(string triggerName, double startTime, double endTime, int groupNumber) - { - var trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber); - triggers.Add(trigger); - return trigger; - } - - protected IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, IEnumerable>? triggeredGroups) - { - var commands = TimelineGroup.GetCommands(timelineSelector); - foreach (var loop in Loops) - commands = commands.Concat(loop.GetCommands(timelineSelector)); - - if (triggeredGroups != null) - { - foreach (var pair in triggeredGroups) - commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2)); - } - - return commands; - } - - public override string ToString() - => $"{Path}, {Origin}, {InitialPosition}"; - } - - public abstract class StoryboardElementWithDuration : StoryboardElementWithDuration - where U : Drawable, IDrawableStoryboardElement - { - private delegate void DrawablePropertyInitializer(U drawable, T value); - - protected StoryboardElementWithDuration(string path, Anchor origin, Vector2 initialPosition) - : base(path, origin, initialPosition) - { - } - - public override Drawable CreateDrawable() => CreateStoryboardDrawable(); - - public abstract U CreateStoryboardDrawable(); - - public void ApplyTransforms(U drawable, IEnumerable>? triggeredGroups = null) - { - // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. - // To achieve this, commands are "generated" as pairs of (command, initFunc, transformFunc) and batched into a contiguous list - // The list is then stably-sorted (to preserve command order), and applied to the drawable sequentially. - - List> generated = new List>(); - - generateCommands(generated, GetCommands(g => g.X, triggeredGroups), (d, value) => d.X = value); - generateCommands(generated, GetCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value); - generateCommands(generated, GetCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value); - generateCommands(generated, GetCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value); - generateCommands(generated, GetCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value); - generateCommands(generated, GetCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value); - generateCommands(generated, GetCommands(g => g.BlendingParameters, triggeredGroups), (d, value) => d.Blending = value, false); - generateCommands(generated, GetCommands(g => g.VectorScale, triggeredGroups), (d, value) => d.VectorScale = value); - generateCommands(generated, GetCommands(g => g.FlipH, triggeredGroups), (d, value) => d.FlipH = value, false); - generateCommands(generated, GetCommands(g => g.FlipV, triggeredGroups), (d, value) => d.FlipV = value, false); - - foreach (var command in generated.OrderBy(g => g.StartTime)) - command.ApplyTo(drawable); - } - - private void generateCommands(List> resultList, IEnumerable.TypedCommand> commands, - DrawablePropertyInitializer initializeProperty, bool alwaysInitialize = true) - { - bool initialized = false; - - foreach (var command in commands) - { - DrawablePropertyInitializer? initFunc = null; - - if (!initialized) - { - if (alwaysInitialize || command.StartTime == command.EndTime) - initFunc = initializeProperty; - initialized = true; - } - - resultList.Add(new GeneratedCommand(command, initFunc)); - } - } - - private interface IGeneratedCommand - where TDrawable : U - { - double StartTime { get; } - - void ApplyTo(TDrawable drawable); - } - - private readonly struct GeneratedCommand : IGeneratedCommand - where TDrawable : U - { - public double StartTime => command.StartTime; - - private readonly DrawablePropertyInitializer? initializeProperty; - private readonly CommandTimeline.TypedCommand command; - - public GeneratedCommand(CommandTimeline.TypedCommand command, DrawablePropertyInitializer? initializeProperty) - { - this.command = command; - this.initializeProperty = initializeProperty; - } - - public void ApplyTo(TDrawable drawable) - { - initializeProperty?.Invoke(drawable, command.StartValue); - - using (drawable.BeginAbsoluteSequence(command.StartTime)) - { - var sequence = command.IsParameterCommand - ? drawable.TransformTo(command.PropertyName, command.StartValue).Delay(command.Duration).TransformTo(command.PropertyName, command.EndValue) - : drawable.TransformTo(command.PropertyName, command.StartValue).Then().TransformTo(command.PropertyName, command.EndValue, command.Duration, command.Easing); - - if (command.LoopCount > 0) - sequence.Loop(command.Delay, command.LoopCount); - } - } - } - } -} diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 8eaab9428d..dfd184a909 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -1,19 +1,164 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; +using osu.Game.Storyboards.Commands; using osu.Game.Storyboards.Drawables; using osuTK; namespace osu.Game.Storyboards { - public class StoryboardSprite : StoryboardElementWithDuration + public class StoryboardSprite : IStoryboardElementWithDuration { - public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition) - : base(path, origin, initialPosition) + private readonly List loopGroups = new List(); + private readonly List triggerGroups = new List(); + + public string Path { get; } + public bool IsDrawable => HasCommands; + + public Anchor Origin; + public Vector2 InitialPosition; + + public readonly StoryboardCommandGroup Group = new StoryboardCommandGroup(); + + public double StartTime { + get + { + // To get the initial start time, we need to check whether the first alpha command to exist (across all loops) has a StartValue of zero. + // A StartValue of zero governs, above all else, the first valid display time of a sprite. + // + // You can imagine that the first command of each type decides that type's start value, so if the initial alpha is zero, + // anything before that point can be ignored (the sprite is not visible after all). + var alphaCommands = new List<(double startTime, bool isZeroStartValue)>(); + + var command = Group.Alpha.FirstOrDefault(); + if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); + + foreach (var loop in loopGroups) + { + command = loop.Alpha.FirstOrDefault(); + if (command != null) alphaCommands.Add((command.StartTime + loop.LoopStartTime, command.StartValue == 0)); + } + + if (alphaCommands.Count > 0) + { + var firstAlpha = alphaCommands.MinBy(t => t.startTime); + + if (firstAlpha.isZeroStartValue) + return firstAlpha.startTime; + } + + return EarliestTransformTime; + } } - public override DrawableStoryboardSprite CreateStoryboardDrawable() => new DrawableStoryboardSprite(this); + public double EarliestTransformTime + { + get + { + // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. + // The sprite's StartTime will be determined by the earliest command, regardless of type. + double earliestStartTime = Group.StartTime; + foreach (var l in loopGroups) + earliestStartTime = Math.Min(earliestStartTime, l.StartTime); + return earliestStartTime; + } + } + + public double EndTime + { + get + { + double latestEndTime = Group.EndTime; + + foreach (var l in loopGroups) + latestEndTime = Math.Max(latestEndTime, l.EndTime); + + return latestEndTime; + } + } + + public double EndTimeForDisplay + { + get + { + double latestEndTime = Group.StartTime; + + foreach (var l in loopGroups) + latestEndTime = Math.Max(latestEndTime, l.StartTime + l.Duration * l.TotalIterations); + + return latestEndTime; + } + } + + public bool HasCommands => Group.HasCommands || loopGroups.Any(l => l.HasCommands); + + public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition) + { + Path = path; + Origin = origin; + InitialPosition = initialPosition; + } + + public virtual Drawable CreateDrawable() => new DrawableStoryboardSprite(this); + + public StoryboardLoopingGroup AddLoopingGroup(double loopStartTime, int repeatCount) + { + var loop = new StoryboardLoopingGroup(loopStartTime, repeatCount); + loopGroups.Add(loop); + return loop; + } + + public StoryboardTriggerGroup AddTriggerGroup(string triggerName, double startTime, double endTime, int groupNumber) + { + var trigger = new StoryboardTriggerGroup(triggerName, startTime, endTime, groupNumber); + triggerGroups.Add(trigger); + return trigger; + } + + public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; + + public void ApplyTransforms(Drawable drawable, IEnumerable>? triggeredGroups = null) + { + // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. + + var commands = Group.GetAllCommands(); + commands = commands.Concat(loopGroups.SelectMany(l => l.GetAllCommands())); + + // todo: triggers are not implemented yet. + // if (triggeredGroups != null) + // commands = commands.Concat(triggeredGroups.SelectMany(tuple => tuple.Item1.GetAllCommands(tuple.Item2))); + + foreach (var command in commands.OrderBy(c => c.StartTime)) + { + using (drawable.BeginAbsoluteSequence(command.StartTime)) + command.ApplyTransform(drawable); + } + } + + // todo: need to revisit property initialisation. apparently it has to be done per first command of every affected property (transforms are supposed to do that already?). + // private void generateCommands(List resultList, IEnumerable.TypedCommand> commands, + // DrawablePropertyInitializer initializeProperty, DrawableTransform transform, bool alwaysInitialize = true) + // { + // bool initialized = false; + // + // foreach (var command in commands) + // { + // DrawablePropertyInitializer? initFunc = null; + // + // if (!initialized) + // { + // if (alwaysInitialize || command.StartTime == command.EndTime) + // initFunc = initializeProperty; + // initialized = true; + // } + // + // resultList.Add(new GeneratedCommand(command, initFunc, transform)); + // } + // } } } From 9b77d8c972abefe0e7e0d524e26a90b457f41abf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 01:59:26 +0300 Subject: [PATCH 057/581] Fix group start/end time not calculating correctly --- osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs index 02c43c9f60..5bb7ee6acf 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -30,13 +30,13 @@ namespace osu.Game.Storyboards.Commands /// Returns the earliest start time of the commands added to this group. /// [JsonIgnore] - public double StartTime { get; private set; } + public double StartTime { get; private set; } = double.MaxValue; /// /// Returns the latest end time of the commands added to this group. /// [JsonIgnore] - public double EndTime { get; private set; } + public double EndTime { get; private set; } = double.MinValue; [JsonIgnore] public double Duration => EndTime - StartTime; From 6c257e515996d04a7e31c35ac9bf7219442e2f2e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 01:59:42 +0300 Subject: [PATCH 058/581] Fix `HasCommands` property not set at all --- osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs index 5bb7ee6acf..0d4a79079b 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -104,6 +104,7 @@ namespace osu.Game.Storyboards.Commands protected virtual void AddCommand(ICollection> list, StoryboardCommand command) { list.Add(command); + HasCommands = true; if (command.StartTime < StartTime) StartTime = command.StartTime; From 87b065b8c3c82a365be4a2b9274042480e417a36 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 02:00:23 +0300 Subject: [PATCH 059/581] Fix incorrect start time calculations `LoopStartTime` is now baked into each `IStoryboardCommand`. --- osu.Game/Storyboards/StoryboardSprite.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index dfd184a909..fc3c5d343c 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -41,7 +41,7 @@ namespace osu.Game.Storyboards foreach (var loop in loopGroups) { command = loop.Alpha.FirstOrDefault(); - if (command != null) alphaCommands.Add((command.StartTime + loop.LoopStartTime, command.StartValue == 0)); + if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); } if (alphaCommands.Count > 0) @@ -120,8 +120,6 @@ namespace osu.Game.Storyboards return trigger; } - public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; - public void ApplyTransforms(Drawable drawable, IEnumerable>? triggeredGroups = null) { // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. @@ -140,6 +138,8 @@ namespace osu.Game.Storyboards } } + public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; + // todo: need to revisit property initialisation. apparently it has to be done per first command of every affected property (transforms are supposed to do that already?). // private void generateCommands(List resultList, IEnumerable.TypedCommand> commands, // DrawablePropertyInitializer initializeProperty, DrawableTransform transform, bool alwaysInitialize = true) From 3755dd059af7855219a1867d894aee803e722fa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 01:58:58 +0300 Subject: [PATCH 060/581] Calculate loop delays at point of transform application --- .../Commands/StoryboardLoopingGroup.cs | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs index e520353bd6..39b81ead28 100644 --- a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs @@ -10,7 +10,7 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardLoopingGroup : StoryboardCommandGroup { - public double LoopStartTime; + private readonly double loopStartTime; /// /// The total number of times this loop is played back. Always greater than zero. @@ -26,45 +26,41 @@ namespace osu.Game.Storyboards.Commands { if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); - LoopStartTime = startTime; + loopStartTime = startTime; TotalIterations = repeatCount + 1; } protected override void AddCommand(ICollection> list, StoryboardCommand command) - { - // todo: this is broke! - double fullLoopDuration = EndTime - StartTime; - double loopDelay = fullLoopDuration - command.EndTime + command.StartTime; - base.AddCommand(list, new StoryboardLoopingCommand(command, LoopStartTime, TotalIterations, loopDelay)); - } + => base.AddCommand(list, new StoryboardLoopingCommand(command, this)); - public override string ToString() => $"{LoopStartTime} x{TotalIterations}"; + public override string ToString() => $"{loopStartTime} x{TotalIterations}"; private class StoryboardLoopingCommand : StoryboardCommand { private readonly StoryboardCommand command; - private readonly int loopCount; - private readonly double loopDelay; + private readonly StoryboardLoopingGroup loopingGroup; - public StoryboardLoopingCommand(StoryboardCommand command, double loopStartTime, int loopCount, double loopDelay) + public StoryboardLoopingCommand(StoryboardCommand command, StoryboardLoopingGroup loopingGroup) // In an ideal world, we would multiply the command duration by TotalIterations in command end time. // Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro // sequences for minutes or hours. - : base(loopStartTime + command.StartTime, loopStartTime + command.EndTime, command.StartValue, command.EndValue, command.Easing) + : base(loopingGroup.loopStartTime + command.StartTime, loopingGroup.loopStartTime + command.EndTime, command.StartValue, command.EndValue, command.Easing) { this.command = command; - this.loopCount = loopCount; - this.loopDelay = loopDelay; + this.loopingGroup = loopingGroup; } - public override void SetInitialValue(Drawable d) => command.SetInitialValue(d); + public override string PropertyName => command.PropertyName; - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => command.ApplyInitialValue(d); + + public override TransformSequence ApplyTransforms(Drawable d) { - if (loopCount == 0) - return command.ApplyTransform(d); + if (loopingGroup.TotalIterations == 0) + return command.ApplyTransforms(d); - return command.ApplyTransform(d).Loop(loopDelay, loopCount); + double loopingGroupDuration = loopingGroup.Duration; + return command.ApplyTransforms(d).Loop(loopingGroupDuration - Duration, loopingGroup.TotalIterations); } } } From 2ca36254f46c3501d97a8782bcc7a699104557d1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 02:02:22 +0300 Subject: [PATCH 061/581] Fix comparison interface not implemented on storyboard command classes --- osu.Game/Storyboards/Commands/StoryboardCommand.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/Commands/StoryboardCommand.cs b/osu.Game/Storyboards/Commands/StoryboardCommand.cs index 883b9f57c1..1647faf243 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommand.cs @@ -1,12 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Storyboards.Commands { - public abstract class StoryboardCommand : IStoryboardCommand + public abstract class StoryboardCommand : IStoryboardCommand, IComparable> { public double StartTime { get; } public double EndTime { get; } @@ -41,10 +42,14 @@ namespace osu.Game.Storyboards.Commands /// public abstract TransformSequence ApplyTransform(Drawable d); - public int CompareTo(IStoryboardCommand other) + public int CompareTo(StoryboardCommand? other) { + if (other == null) + return 1; + int result = StartTime.CompareTo(other.StartTime); - if (result != 0) return result; + if (result != 0) + return result; return EndTime.CompareTo(other.EndTime); } From b450abb687823132c9bb3c6b1041bb70bb05d8ea Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 02:02:45 +0300 Subject: [PATCH 062/581] Support applying initial values of storyboard commands --- .../Storyboards/Commands/IStoryboardCommand.cs | 14 +++++++++++++- .../Storyboards/Commands/StoryboardAlphaCommand.cs | 8 ++++++-- .../StoryboardBlendingParametersCommand.cs | 9 ++++++--- .../Commands/StoryboardColourCommand.cs | 8 ++++++-- osu.Game/Storyboards/Commands/StoryboardCommand.cs | 12 ++++-------- .../Storyboards/Commands/StoryboardFlipHCommand.cs | 6 ++++-- .../Storyboards/Commands/StoryboardFlipVCommand.cs | 6 ++++-- .../Commands/StoryboardRotationCommand.cs | 6 ++++-- .../Storyboards/Commands/StoryboardScaleCommand.cs | 6 ++++-- .../Commands/StoryboardVectorScaleCommand.cs | 6 ++++-- .../Storyboards/Commands/StoryboardXCommand.cs | 6 ++++-- .../Storyboards/Commands/StoryboardYCommand.cs | 6 ++++-- osu.Game/Storyboards/StoryboardSprite.cs | 10 +++++++++- 13 files changed, 72 insertions(+), 31 deletions(-) diff --git a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs index 848dcab575..05613a987d 100644 --- a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs @@ -18,11 +18,23 @@ namespace osu.Game.Storyboards.Commands /// double EndTime { get; } + /// + /// The name of the property affected by this storyboard command. + /// Used to apply initial property values based on the list of commands given in . + /// + string PropertyName { get; } + + /// + /// Sets the value of the corresponding property in to the start value of this command. + /// + /// The target drawable. + void ApplyInitialValue(Drawable d); + /// /// Applies the transforms described by this storyboard command to the target drawable. /// /// The target drawable. /// The sequence of transforms applied to the target drawable. - TransformSequence ApplyTransform(Drawable d); + TransformSequence ApplyTransforms(Drawable d); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs index 729ecd72a7..1d91d6ccc1 100644 --- a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs @@ -13,7 +13,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Alpha = StartValue; - public override TransformSequence ApplyTransform(Drawable d) => d.FadeTo(StartValue).Then().FadeTo(EndValue, Duration, Easing); + public override string PropertyName => nameof(Drawable.Alpha); + + public override void ApplyInitialValue(Drawable d) => d.Alpha = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) + => d.FadeTo(StartValue).Then().FadeTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs index cc54909837..3a2d372a66 100644 --- a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs @@ -13,9 +13,12 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Blending = StartValue; + public override string PropertyName => nameof(Drawable.Blending); - public override TransformSequence ApplyTransform(Drawable d) - => d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration).TransformTo(nameof(d.Blending), EndValue); + public override void ApplyInitialValue(Drawable d) => d.Blending = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) + => d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration) + .TransformTo(nameof(d.Blending), EndValue); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs index be56a1d71b..fbde7e1af7 100644 --- a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs @@ -14,7 +14,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Colour = StartValue; - public override TransformSequence ApplyTransform(Drawable d) => d.FadeColour(StartValue).Then().FadeColour(EndValue, Duration, Easing); + public override string PropertyName => nameof(Drawable.Colour); + + public override void ApplyInitialValue(Drawable d) => d.Colour = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) + => d.FadeColour(StartValue).Then().FadeColour(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardCommand.cs b/osu.Game/Storyboards/Commands/StoryboardCommand.cs index 1647faf243..58fcb148ff 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommand.cs @@ -32,15 +32,11 @@ namespace osu.Game.Storyboards.Commands public T StartValue; public T EndValue; - /// - /// Sets the value of the corresponding property in to the start value of this command. - /// - public abstract void SetInitialValue(Drawable d); + public abstract string PropertyName { get; } - /// - /// Transforms a corresponding property in that corresponds to this command group with the specified parameters. - /// - public abstract TransformSequence ApplyTransform(Drawable d); + public abstract void ApplyInitialValue(Drawable d); + + public abstract TransformSequence ApplyTransforms(Drawable d); public int CompareTo(StoryboardCommand? other) { diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs index 9bcb687d3c..c381b0bb64 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs @@ -14,9 +14,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipH = StartValue; + public override string PropertyName => nameof(IDrawableStoryboardElement.FlipH); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipH = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.TransformTo(nameof(IDrawableStoryboardElement.FlipH), StartValue).Delay(Duration) .TransformTo(nameof(IDrawableStoryboardElement.FlipH), EndValue); } diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs index 9f1f5faa33..e43e5e9205 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs @@ -14,9 +14,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipV = StartValue; + public override string PropertyName => nameof(IDrawableStoryboardElement.FlipV); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipV = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.TransformTo(nameof(IDrawableStoryboardElement.FlipV), StartValue).Delay(Duration) .TransformTo(nameof(IDrawableStoryboardElement.FlipV), EndValue); } diff --git a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs index c56dcd130f..2a449af843 100644 --- a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs @@ -13,9 +13,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Rotation = StartValue; + public override string PropertyName => nameof(Drawable.Rotation); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => d.Rotation = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.RotateTo(StartValue).Then().RotateTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs index 9dbdd6ebd8..bf9796148c 100644 --- a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs @@ -14,9 +14,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Scale = new Vector2(StartValue); + public override string PropertyName => nameof(Drawable.Scale); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => d.Scale = new Vector2(StartValue); + + public override TransformSequence ApplyTransforms(Drawable d) => d.ScaleTo(StartValue).Then().ScaleTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs index fefb21b257..638dc6a5ee 100644 --- a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs @@ -15,9 +15,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).VectorScale = StartValue; + public override string PropertyName => nameof(IDrawableStoryboardElement.VectorScale); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).VectorScale = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.TransformTo(nameof(IDrawableStoryboardElement.VectorScale), StartValue).Then() .TransformTo(nameof(IDrawableStoryboardElement.VectorScale), EndValue, Duration, Easing); } diff --git a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs index a9f9cd4f8f..809a77256c 100644 --- a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs @@ -13,9 +13,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.X = StartValue; + public override string PropertyName => nameof(Drawable.X); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => d.X = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.MoveToX(StartValue).Then().MoveToX(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs index eb30b36720..d054135878 100644 --- a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs @@ -13,9 +13,11 @@ namespace osu.Game.Storyboards.Commands { } - public override void SetInitialValue(Drawable d) => d.Y = StartValue; + public override string PropertyName => nameof(Drawable.Y); - public override TransformSequence ApplyTransform(Drawable d) + public override void ApplyInitialValue(Drawable d) => d.Y = StartValue; + + public override TransformSequence ApplyTransforms(Drawable d) => d.MoveToY(StartValue).Then().MoveToY(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index fc3c5d343c..9d7ab66692 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -131,10 +131,18 @@ namespace osu.Game.Storyboards // if (triggeredGroups != null) // commands = commands.Concat(triggeredGroups.SelectMany(tuple => tuple.Item1.GetAllCommands(tuple.Item2))); + HashSet appliedProperties = new HashSet(); + foreach (var command in commands.OrderBy(c => c.StartTime)) { + if (!appliedProperties.Contains(command.PropertyName)) + { + command.ApplyInitialValue(drawable); + appliedProperties.Add(command.PropertyName); + } + using (drawable.BeginAbsoluteSequence(command.StartTime)) - command.ApplyTransform(drawable); + command.ApplyTransforms(drawable); } } From fa9b2f0cd541bb4c80c895a0bbeced418ccd85e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 03:06:49 +0300 Subject: [PATCH 063/581] Add generics to `ApplyInitialValue`/`ApplyTransforms` for ability to return custom transform sequences *sigh* --- .../Visual/Gameplay/TestSceneLeadIn.cs | 8 +-- .../Gameplay/TestSceneStoryboardWithOutro.cs | 2 +- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Formats/LegacyStoryboardDecoder.cs | 32 ++++----- .../Commands/IStoryboardCommand.cs | 7 +- .../Commands/StoryboardAlphaCommand.cs | 4 +- .../StoryboardBlendingParametersCommand.cs | 4 +- .../Commands/StoryboardColourCommand.cs | 4 +- .../Storyboards/Commands/StoryboardCommand.cs | 19 +++--- .../Commands/StoryboardCommandGroup.cs | 48 ++++--------- .../Commands/StoryboardCommandList.cs | 41 ------------ .../Commands/StoryboardFlipHCommand.cs | 10 +-- .../Commands/StoryboardFlipVCommand.cs | 10 +-- .../Commands/StoryboardLoopingGroup.cs | 5 +- .../Commands/StoryboardRotationCommand.cs | 4 +- .../Commands/StoryboardScaleCommand.cs | 4 +- .../Commands/StoryboardTriggerGroup.cs | 1 - .../Commands/StoryboardVectorScaleCommand.cs | 10 +-- .../Commands/StoryboardXCommand.cs | 4 +- .../Commands/StoryboardYCommand.cs | 4 +- .../Drawables/DrawableStoryboardAnimation.cs | 2 +- .../Drawables/DrawableStoryboardSprite.cs | 6 +- ...ableStoryboardElement.cs => IFlippable.cs} | 4 +- .../Storyboards/Drawables/IVectorScalable.cs | 13 ++++ osu.Game/Storyboards/StoryboardSprite.cs | 67 ++++++------------- 25 files changed, 122 insertions(+), 193 deletions(-) delete mode 100644 osu.Game/Storyboards/Commands/StoryboardCommandList.cs rename osu.Game/Storyboards/Drawables/{IDrawableStoryboardElement.cs => IFlippable.cs} (72%) create mode 100644 osu.Game/Storyboards/Drawables/IVectorScalable.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index c3eef4da9b..b2196e77b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Group.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None); + sprite.Commands.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None); storyboard.GetLayer("Background").Add(sprite); @@ -73,16 +73,16 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); // these should be ignored as we have an alpha visibility blocker proceeding this command. - sprite.Group.AddScale(loop_start_time, -18000, 0, 1, Easing.None); + sprite.Commands.AddScale(loop_start_time, -18000, 0, 1, Easing.None); var loopGroup = sprite.AddLoopingGroup(loop_start_time, 50); loopGroup.AddScale(loop_start_time, -18000, 0, 1, Easing.None); - var target = addEventToLoop ? loopGroup : sprite.Group; + var target = addEventToLoop ? loopGroup : sprite.Commands; double loopRelativeOffset = addEventToLoop ? -loop_start_time : 0; target.AddAlpha(loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1, Easing.None); // these should be ignored due to being in the future. - sprite.Group.AddAlpha(18000, 20000, 0, 1, Easing.None); + sprite.Commands.AddAlpha(18000, 20000, 0, 1, Easing.None); loopGroup.AddAlpha(38000, 40000, 0, 1, Easing.None); storyboard.GetLayer("Background").Add(sprite); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 9269c3f4ae..8bdb7297fe 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var storyboard = new Storyboard(); var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Group.AddAlpha(0, duration, 1, 0, Easing.None); + sprite.Commands.AddAlpha(0, duration, 1, 0, Easing.None); storyboard.GetLayer("Background").Add(sprite); return storyboard; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 62a2bfeaab..7cd2e2fdaa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -424,7 +424,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestIntroStoryboardElement() => testLeadIn(b => { var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Group.AddAlpha(-2000, 0, 0, 1, Easing.None); + sprite.Commands.AddAlpha(-2000, 0, 0, 1, Easing.None); b.Storyboard.GetLayer("Background").Add(sprite); }); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 33cdaa085e..83277b71c8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.Formats public class LegacyStoryboardDecoder : LegacyDecoder { private StoryboardSprite? storyboardSprite; - private StoryboardCommandGroup? currentGroup; + private StoryboardCommandGroup? currentCommandsGroup; private Storyboard storyboard = null!; @@ -166,7 +166,7 @@ namespace osu.Game.Beatmaps.Formats else { if (depth < 2) - currentGroup = storyboardSprite?.Group; + currentCommandsGroup = storyboardSprite?.Commands; string commandType = split[0]; @@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats double startTime = split.Length > 2 ? Parsing.ParseDouble(split[2]) : double.MinValue; double endTime = split.Length > 3 ? Parsing.ParseDouble(split[3]) : double.MaxValue; int groupNumber = split.Length > 4 ? Parsing.ParseInt(split[4]) : 0; - currentGroup = storyboardSprite?.AddTriggerGroup(triggerName, startTime, endTime, groupNumber); + currentCommandsGroup = storyboardSprite?.AddTriggerGroup(triggerName, startTime, endTime, groupNumber); break; } @@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats { double startTime = Parsing.ParseDouble(split[1]); int repeatCount = Parsing.ParseInt(split[2]); - currentGroup = storyboardSprite?.AddLoopingGroup(startTime, Math.Max(0, repeatCount - 1)); + currentCommandsGroup = storyboardSprite?.AddLoopingGroup(startTime, Math.Max(0, repeatCount - 1)); break; } @@ -205,7 +205,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing); break; } @@ -213,7 +213,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentGroup?.AddScale(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddScale(startTime, endTime, startValue, endValue, easing); break; } @@ -223,7 +223,7 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - currentGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing); + currentCommandsGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing); break; } @@ -231,7 +231,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentGroup?.AddRotation(startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue), easing); + currentCommandsGroup?.AddRotation(startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue), easing); break; } @@ -241,8 +241,8 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - currentGroup?.AddX(startTime, endTime, startX, endX, easing); - currentGroup?.AddY(startTime, endTime, startY, endY, easing); + currentCommandsGroup?.AddX(startTime, endTime, startX, endX, easing); + currentCommandsGroup?.AddY(startTime, endTime, startY, endY, easing); break; } @@ -250,7 +250,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentGroup?.AddX(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddX(startTime, endTime, startValue, endValue, easing); break; } @@ -258,7 +258,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentGroup?.AddY(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddY(startTime, endTime, startValue, endValue, easing); break; } @@ -270,7 +270,7 @@ namespace osu.Game.Beatmaps.Formats float endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed; float endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen; float endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue; - currentGroup?.AddColour(startTime, endTime, + currentCommandsGroup?.AddColour(startTime, endTime, new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1), easing); break; @@ -283,16 +283,16 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case "A": - currentGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive, + currentCommandsGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive, startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit, easing); break; case "H": - currentGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing); + currentCommandsGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing); break; case "V": - currentGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing); + currentCommandsGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing); break; } diff --git a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs index 05613a987d..6efb19afe4 100644 --- a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards.Commands { @@ -28,13 +29,15 @@ namespace osu.Game.Storyboards.Commands /// Sets the value of the corresponding property in to the start value of this command. /// /// The target drawable. - void ApplyInitialValue(Drawable d); + void ApplyInitialValue(TDrawable d) + where TDrawable : Drawable, IFlippable, IVectorScalable; /// /// Applies the transforms described by this storyboard command to the target drawable. /// /// The target drawable. /// The sequence of transforms applied to the target drawable. - TransformSequence ApplyTransforms(Drawable d); + TransformSequence ApplyTransforms(TDrawable d) + where TDrawable : Drawable, IFlippable, IVectorScalable; } } diff --git a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs index 1d91d6ccc1..f4a90b372a 100644 --- a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs @@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Alpha); - public override void ApplyInitialValue(Drawable d) => d.Alpha = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.Alpha = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.FadeTo(StartValue).Then().FadeTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs index 3a2d372a66..3e2e8bb0e8 100644 --- a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs @@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Blending); - public override void ApplyInitialValue(Drawable d) => d.Blending = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.Blending = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration) .TransformTo(nameof(d.Blending), EndValue); } diff --git a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs index fbde7e1af7..66390eb305 100644 --- a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs @@ -16,9 +16,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Colour); - public override void ApplyInitialValue(Drawable d) => d.Colour = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.Colour = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.FadeColour(StartValue).Then().FadeColour(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardCommand.cs b/osu.Game/Storyboards/Commands/StoryboardCommand.cs index 58fcb148ff..4f2f0f04a2 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommand.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; +using osu.Game.Storyboards.Drawables; namespace osu.Game.Storyboards.Commands { @@ -11,6 +12,11 @@ namespace osu.Game.Storyboards.Commands { public double StartTime { get; } public double EndTime { get; } + + public T StartValue { get; } + public T EndValue { get; } + public Easing Easing { get; } + public double Duration => EndTime - StartTime; protected StoryboardCommand(double startTime, double endTime, T startValue, T endValue, Easing easing) @@ -25,18 +31,13 @@ namespace osu.Game.Storyboards.Commands Easing = easing; } - public Easing Easing { get; set; } - public int LoopCount { get; set; } - public double Delay { get; set; } - - public T StartValue; - public T EndValue; - public abstract string PropertyName { get; } - public abstract void ApplyInitialValue(Drawable d); + public abstract void ApplyInitialValue(TDrawable d) + where TDrawable : Drawable, IFlippable, IVectorScalable; - public abstract TransformSequence ApplyTransforms(Drawable d); + public abstract TransformSequence ApplyTransforms(TDrawable d) + where TDrawable : Drawable, IFlippable, IVectorScalable; public int CompareTo(StoryboardCommand? other) { diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs index 0d4a79079b..fb847d2e44 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Lists; @@ -13,18 +12,20 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardCommandGroup { - public SortedList> X; - public SortedList> Y; - public SortedList> Scale; - public SortedList> VectorScale; - public SortedList> Rotation; - public SortedList> Colour; - public SortedList> Alpha; - public SortedList> BlendingParameters; - public SortedList> FlipH; - public SortedList> FlipV; + public SortedList> X = new SortedList>(); + public SortedList> Y = new SortedList>(); + public SortedList> Scale = new SortedList>(); + public SortedList> VectorScale = new SortedList>(); + public SortedList> Rotation = new SortedList>(); + public SortedList> Colour = new SortedList>(); + public SortedList> Alpha = new SortedList>(); + public SortedList> BlendingParameters = new SortedList>(); + public SortedList> FlipH = new SortedList>(); + public SortedList> FlipV = new SortedList>(); - private readonly IReadOnlyList[] lists; + public IReadOnlyList AllCommands => allCommands; + + private readonly List allCommands = new List(); /// /// Returns the earliest start time of the commands added to this group. @@ -44,28 +45,6 @@ namespace osu.Game.Storyboards.Commands [JsonIgnore] public bool HasCommands { get; private set; } - public StoryboardCommandGroup() - { - lists = new IReadOnlyList[] - { - X = new SortedList>(), - Y = new SortedList>(), - Scale = new SortedList>(), - VectorScale = new SortedList>(), - Rotation = new SortedList>(), - Colour = new SortedList>(), - Alpha = new SortedList>(), - BlendingParameters = new SortedList>(), - FlipH = new SortedList>(), - FlipV = new SortedList>() - }; - } - - /// - /// Returns all commands contained by this group unsorted. - /// - public virtual IEnumerable GetAllCommands() => lists.SelectMany(l => l); - public void AddX(double startTime, double endTime, float startValue, float endValue, Easing easing) => AddCommand(X, new StoryboardXCommand(startTime, endTime, startValue, endValue, easing)); @@ -104,6 +83,7 @@ namespace osu.Game.Storyboards.Commands protected virtual void AddCommand(ICollection> list, StoryboardCommand command) { list.Add(command); + allCommands.Add(command); HasCommands = true; if (command.StartTime < StartTime) diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandList.cs b/osu.Game/Storyboards/Commands/StoryboardCommandList.cs deleted file mode 100644 index 67012e9d49..0000000000 --- a/osu.Game/Storyboards/Commands/StoryboardCommandList.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Storyboards.Commands -{ - // public class StoryboardCommandList : IStoryboardCommandList - // { - // // todo: change to sorted list and avoid enumerable type on exposed properties? - // private readonly List> commands = new List>(); - // - // public IEnumerable> Commands => commands.OrderBy(c => c.StartTime); - // - // IEnumerable IStoryboardCommandList.Commands => Commands; - // public bool HasCommands => commands.Count > 0; - // - // public double StartTime { get; private set; } = double.MaxValue; - // public double EndTime { get; private set; } = double.MinValue; - // - // public T? StartValue { get; private set; } - // public T? EndValue { get; private set; } - // - // public void Add(StoryboardCommand command) - // { - // commands.Add(command); - // - // if (command.StartTime < StartTime) - // { - // StartValue = command.StartValue; - // StartTime = command.StartTime; - // } - // - // if (command.EndTime > EndTime) - // { - // EndValue = command.EndValue; - // EndTime = command.EndTime; - // } - // } - // - // public override string ToString() => $"{commands.Count} command(s)"; - // } -} diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs index c381b0bb64..26aef23226 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs @@ -14,12 +14,12 @@ namespace osu.Game.Storyboards.Commands { } - public override string PropertyName => nameof(IDrawableStoryboardElement.FlipH); + public override string PropertyName => nameof(IFlippable.FlipH); - public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipH = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.FlipH = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) - => d.TransformTo(nameof(IDrawableStoryboardElement.FlipH), StartValue).Delay(Duration) - .TransformTo(nameof(IDrawableStoryboardElement.FlipH), EndValue); + public override TransformSequence ApplyTransforms(TDrawable d) + => d.TransformTo(nameof(IFlippable.FlipH), StartValue).Delay(Duration) + .TransformTo(nameof(IFlippable.FlipH), EndValue); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs index e43e5e9205..88423da2af 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs @@ -14,12 +14,12 @@ namespace osu.Game.Storyboards.Commands { } - public override string PropertyName => nameof(IDrawableStoryboardElement.FlipV); + public override string PropertyName => nameof(IFlippable.FlipV); - public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipV = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.FlipV = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) - => d.TransformTo(nameof(IDrawableStoryboardElement.FlipV), StartValue).Delay(Duration) - .TransformTo(nameof(IDrawableStoryboardElement.FlipV), EndValue); + public override TransformSequence ApplyTransforms(TDrawable d) + => d.TransformTo(nameof(IFlippable.FlipV), StartValue).Delay(Duration) + .TransformTo(nameof(IFlippable.FlipV), EndValue); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs index 39b81ead28..e97de84ab7 100644 --- a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Storyboards.Commands @@ -52,9 +51,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => command.PropertyName; - public override void ApplyInitialValue(Drawable d) => command.ApplyInitialValue(d); + public override void ApplyInitialValue(TDrawable d) => command.ApplyInitialValue(d); - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) { if (loopingGroup.TotalIterations == 0) return command.ApplyTransforms(d); diff --git a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs index 2a449af843..4347dc9d77 100644 --- a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs @@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Rotation); - public override void ApplyInitialValue(Drawable d) => d.Rotation = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.Rotation = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.RotateTo(StartValue).Then().RotateTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs index bf9796148c..b0f33fd6b8 100644 --- a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs @@ -16,9 +16,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Scale); - public override void ApplyInitialValue(Drawable d) => d.Scale = new Vector2(StartValue); + public override void ApplyInitialValue(TDrawable d) => d.Scale = new Vector2(StartValue); - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.ScaleTo(StartValue).Then().ScaleTo(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs b/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs index dfb6f8cb1b..89a68e9ec0 100644 --- a/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardTriggerGroup.cs @@ -3,7 +3,6 @@ namespace osu.Game.Storyboards.Commands { - // todo: this is not implemented and has never been, keep that in mind. public class StoryboardTriggerGroup : StoryboardCommandGroup { public string TriggerName; diff --git a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs index 638dc6a5ee..5d3fef5948 100644 --- a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs @@ -15,12 +15,12 @@ namespace osu.Game.Storyboards.Commands { } - public override string PropertyName => nameof(IDrawableStoryboardElement.VectorScale); + public override string PropertyName => nameof(IVectorScalable.VectorScale); - public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).VectorScale = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.VectorScale = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) - => d.TransformTo(nameof(IDrawableStoryboardElement.VectorScale), StartValue).Then() - .TransformTo(nameof(IDrawableStoryboardElement.VectorScale), EndValue, Duration, Easing); + public override TransformSequence ApplyTransforms(TDrawable d) + => d.TransformTo(nameof(d.VectorScale), StartValue).Then() + .TransformTo(nameof(d.VectorScale), EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs index 809a77256c..7df9a75768 100644 --- a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs @@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.X); - public override void ApplyInitialValue(Drawable d) => d.X = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.X = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.MoveToX(StartValue).Then().MoveToX(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs index d054135878..d7dc32a0f3 100644 --- a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs @@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Y); - public override void ApplyInitialValue(Drawable d) => d.Y = StartValue; + public override void ApplyInitialValue(TDrawable d) => d.Y = StartValue; - public override TransformSequence ApplyTransforms(Drawable d) + public override TransformSequence ApplyTransforms(TDrawable d) => d.MoveToY(StartValue).Then().MoveToY(EndValue, Duration, Easing); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 8e1a8ce949..fae9ec7f2e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -16,7 +16,7 @@ using osuTK; namespace osu.Game.Storyboards.Drawables { - public partial class DrawableStoryboardAnimation : TextureAnimation, IDrawableStoryboardElement + public partial class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable { public StoryboardAnimation Animation { get; } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index 6772178e85..507a51aca4 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -13,7 +14,7 @@ using osuTK; namespace osu.Game.Storyboards.Drawables { - public partial class DrawableStoryboardSprite : Sprite, IDrawableStoryboardElement + public partial class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable { public StoryboardSprite Sprite { get; } @@ -101,6 +102,9 @@ namespace osu.Game.Storyboards.Drawables else Texture = textureStore.Get(Sprite.Path); + if (Sprite.Path == "SB/textbox.png") + Debugger.Break(); + Sprite.ApplyTransforms(this); } diff --git a/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs similarity index 72% rename from osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs rename to osu.Game/Storyboards/Drawables/IFlippable.cs index 04bae88c76..79f98ea6ef 100644 --- a/osu.Game/Storyboards/Drawables/IDrawableStoryboardElement.cs +++ b/osu.Game/Storyboards/Drawables/IFlippable.cs @@ -2,14 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osuTK; namespace osu.Game.Storyboards.Drawables { - public interface IDrawableStoryboardElement : IDrawable + public interface IFlippable : IDrawable { bool FlipH { get; set; } bool FlipV { get; set; } - Vector2 VectorScale { get; set; } } } diff --git a/osu.Game/Storyboards/Drawables/IVectorScalable.cs b/osu.Game/Storyboards/Drawables/IVectorScalable.cs new file mode 100644 index 0000000000..ce6047c8f6 --- /dev/null +++ b/osu.Game/Storyboards/Drawables/IVectorScalable.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osuTK; + +namespace osu.Game.Storyboards.Drawables +{ + public interface IVectorScalable : IDrawable + { + Vector2 VectorScale { get; set; } + } +} diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 9d7ab66692..f2c011bfca 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -13,7 +13,7 @@ namespace osu.Game.Storyboards { public class StoryboardSprite : IStoryboardElementWithDuration { - private readonly List loopGroups = new List(); + private readonly List loopingGroups = new List(); private readonly List triggerGroups = new List(); public string Path { get; } @@ -22,7 +22,7 @@ namespace osu.Game.Storyboards public Anchor Origin; public Vector2 InitialPosition; - public readonly StoryboardCommandGroup Group = new StoryboardCommandGroup(); + public readonly StoryboardCommandGroup Commands = new StoryboardCommandGroup(); public double StartTime { @@ -35,10 +35,10 @@ namespace osu.Game.Storyboards // anything before that point can be ignored (the sprite is not visible after all). var alphaCommands = new List<(double startTime, bool isZeroStartValue)>(); - var command = Group.Alpha.FirstOrDefault(); + var command = Commands.Alpha.FirstOrDefault(); if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); - foreach (var loop in loopGroups) + foreach (var loop in loopingGroups) { command = loop.Alpha.FirstOrDefault(); if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0)); @@ -62,8 +62,8 @@ namespace osu.Game.Storyboards { // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. // The sprite's StartTime will be determined by the earliest command, regardless of type. - double earliestStartTime = Group.StartTime; - foreach (var l in loopGroups) + double earliestStartTime = Commands.StartTime; + foreach (var l in loopingGroups) earliestStartTime = Math.Min(earliestStartTime, l.StartTime); return earliestStartTime; } @@ -73,9 +73,9 @@ namespace osu.Game.Storyboards { get { - double latestEndTime = Group.EndTime; + double latestEndTime = Commands.EndTime; - foreach (var l in loopGroups) + foreach (var l in loopingGroups) latestEndTime = Math.Max(latestEndTime, l.EndTime); return latestEndTime; @@ -86,16 +86,16 @@ namespace osu.Game.Storyboards { get { - double latestEndTime = Group.StartTime; + double latestEndTime = Commands.StartTime; - foreach (var l in loopGroups) + foreach (var l in loopingGroups) latestEndTime = Math.Max(latestEndTime, l.StartTime + l.Duration * l.TotalIterations); return latestEndTime; } } - public bool HasCommands => Group.HasCommands || loopGroups.Any(l => l.HasCommands); + public bool HasCommands => Commands.HasCommands || loopingGroups.Any(l => l.HasCommands); public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition) { @@ -109,7 +109,7 @@ namespace osu.Game.Storyboards public StoryboardLoopingGroup AddLoopingGroup(double loopStartTime, int repeatCount) { var loop = new StoryboardLoopingGroup(loopStartTime, repeatCount); - loopGroups.Add(loop); + loopingGroups.Add(loop); return loop; } @@ -120,26 +120,20 @@ namespace osu.Game.Storyboards return trigger; } - public void ApplyTransforms(Drawable drawable, IEnumerable>? triggeredGroups = null) + public void ApplyTransforms(TDrawable drawable) + where TDrawable : Drawable, IFlippable, IVectorScalable { - // For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. - - var commands = Group.GetAllCommands(); - commands = commands.Concat(loopGroups.SelectMany(l => l.GetAllCommands())); - - // todo: triggers are not implemented yet. - // if (triggeredGroups != null) - // commands = commands.Concat(triggeredGroups.SelectMany(tuple => tuple.Item1.GetAllCommands(tuple.Item2))); - HashSet appliedProperties = new HashSet(); + // For performance reasons, we need to apply the commands in chronological order. + // Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity. + IEnumerable commands = Commands.AllCommands; + commands = commands.Concat(loopingGroups.SelectMany(l => l.AllCommands)); + foreach (var command in commands.OrderBy(c => c.StartTime)) { - if (!appliedProperties.Contains(command.PropertyName)) - { + if (appliedProperties.Add(command.PropertyName)) command.ApplyInitialValue(drawable); - appliedProperties.Add(command.PropertyName); - } using (drawable.BeginAbsoluteSequence(command.StartTime)) command.ApplyTransforms(drawable); @@ -147,26 +141,5 @@ namespace osu.Game.Storyboards } public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; - - // todo: need to revisit property initialisation. apparently it has to be done per first command of every affected property (transforms are supposed to do that already?). - // private void generateCommands(List resultList, IEnumerable.TypedCommand> commands, - // DrawablePropertyInitializer initializeProperty, DrawableTransform transform, bool alwaysInitialize = true) - // { - // bool initialized = false; - // - // foreach (var command in commands) - // { - // DrawablePropertyInitializer? initFunc = null; - // - // if (!initialized) - // { - // if (alwaysInitialize || command.StartTime == command.EndTime) - // initFunc = initializeProperty; - // initialized = true; - // } - // - // resultList.Add(new GeneratedCommand(command, initFunc, transform)); - // } - // } } } From 79da6d861326ca9d6d897d7a0059471a4548d8bf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 03:07:59 +0300 Subject: [PATCH 064/581] Fix refactor error on `EndTimeForDisplay` Not sure when this happened >.> --- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index f2c011bfca..f961019883 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -86,7 +86,7 @@ namespace osu.Game.Storyboards { get { - double latestEndTime = Commands.StartTime; + double latestEndTime = Commands.EndTime; foreach (var l in loopingGroups) latestEndTime = Math.Max(latestEndTime, l.StartTime + l.Duration * l.TotalIterations); From 48c83195677736a41d1bb3b8e8d563247481de72 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 8 Mar 2024 16:01:57 +0100 Subject: [PATCH 065/581] change multiplier to 0.9x --- osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs index 4ecb94467e..81973e65cc 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModConstantSpeed.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Name => "Constant Speed"; public override string Acronym => "CS"; - public override double ScoreMultiplier => 0.8; + public override double ScoreMultiplier => 0.9; public override LocalisableString Description => "No more tricky speed changes!"; public override IconUsage? Icon => FontAwesome.Solid.Equals; public override ModType Type => ModType.Conversion; From a8792b35850c53a5946e6e20176282dc33292075 Mon Sep 17 00:00:00 2001 From: Hivie Date: Fri, 8 Mar 2024 16:02:17 +0100 Subject: [PATCH 066/581] better assertion --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index c88bbec9bc..ee7acec65c 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected virtual double ComputeTimeRange() { // Adjust when we're using constant algorithm to not be sluggish. - double multiplier = VisualisationMethod == ScrollVisualisationMethod.Overlapping ? 1 : 4 * Beatmap.Difficulty.SliderMultiplier; + double multiplier = VisualisationMethod == ScrollVisualisationMethod.Constant ? 4 * Beatmap.Difficulty.SliderMultiplier : 1; return PlayfieldAdjustmentContainer.ComputeTimeRange() / multiplier; } From a85be2a46d574fe62416e7313afa3bfdd3873dd0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 20:22:26 +0300 Subject: [PATCH 067/581] Fix merge conflicts --- osu.Game/Storyboards/StoryboardSprite.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index a6312ccb79..944d77e745 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -97,10 +97,10 @@ namespace osu.Game.Storyboards // If the logic above fails to find anything or discarded by the fact that there are loops present, latestEndTime will be double.MaxValue // and thus conservativeEndTime will be used. - double conservativeEndTime = TimelineGroup.EndTime; + double conservativeEndTime = Commands.EndTime; - foreach (var l in loops) - conservativeEndTime = Math.Max(conservativeEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); + foreach (var l in loopingGroups) + conservativeEndTime = Math.Max(conservativeEndTime, l.StartTime + l.Duration * l.TotalIterations); return Math.Min(latestEndTime, conservativeEndTime); } From c1649b76d638dbe0f32217be7027e7a98b5b6026 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 21:33:46 +0300 Subject: [PATCH 068/581] Reorder command properties to match general format I had them shuffled around in the middle of the refactor. --- .../TestSceneDrawableStoryboardSprite.cs | 2 +- .../Visual/Gameplay/TestSceneLeadIn.cs | 12 +++--- .../Gameplay/TestSceneStoryboardWithOutro.cs | 2 +- .../TestSceneMultiSpectatorScreen.cs | 2 +- .../Formats/LegacyStoryboardDecoder.cs | 28 ++++++------- .../Commands/StoryboardAlphaCommand.cs | 4 +- .../StoryboardBlendingParametersCommand.cs | 4 +- .../Commands/StoryboardColourCommand.cs | 4 +- .../Storyboards/Commands/StoryboardCommand.cs | 2 +- .../Commands/StoryboardCommandGroup.cs | 40 +++++++++---------- .../Commands/StoryboardFlipHCommand.cs | 4 +- .../Commands/StoryboardFlipVCommand.cs | 4 +- .../Commands/StoryboardLoopingGroup.cs | 2 +- .../Commands/StoryboardRotationCommand.cs | 4 +- .../Commands/StoryboardScaleCommand.cs | 4 +- .../Commands/StoryboardVectorScaleCommand.cs | 4 +- .../Commands/StoryboardXCommand.cs | 4 +- .../Commands/StoryboardYCommand.cs | 4 +- 18 files changed, 65 insertions(+), 65 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 6209b42cbb..6bfa141d85 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite(lookupName, origin, initialPosition); var loop = sprite.AddLoopingGroup(Time.Current, 100); - loop.AddAlpha(0, 10000, 1, 1, Easing.None); + loop.AddAlpha(Easing.None, 0, 10000, 1, 1); layer.Elements.Clear(); layer.Add(sprite); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs index b2196e77b4..5a71369976 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneLeadIn.cs @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Commands.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None); + sprite.Commands.AddAlpha(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1); storyboard.GetLayer("Background").Add(sprite); @@ -73,17 +73,17 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); // these should be ignored as we have an alpha visibility blocker proceeding this command. - sprite.Commands.AddScale(loop_start_time, -18000, 0, 1, Easing.None); + sprite.Commands.AddScale(Easing.None, loop_start_time, -18000, 0, 1); var loopGroup = sprite.AddLoopingGroup(loop_start_time, 50); - loopGroup.AddScale(loop_start_time, -18000, 0, 1, Easing.None); + loopGroup.AddScale(Easing.None, loop_start_time, -18000, 0, 1); var target = addEventToLoop ? loopGroup : sprite.Commands; double loopRelativeOffset = addEventToLoop ? -loop_start_time : 0; - target.AddAlpha(loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1, Easing.None); + target.AddAlpha(Easing.None, loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1); // these should be ignored due to being in the future. - sprite.Commands.AddAlpha(18000, 20000, 0, 1, Easing.None); - loopGroup.AddAlpha(38000, 40000, 0, 1, Easing.None); + sprite.Commands.AddAlpha(Easing.None, 18000, 20000, 0, 1); + loopGroup.AddAlpha(Easing.None, 38000, 40000, 0, 1); storyboard.GetLayer("Background").Add(sprite); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs index 8bdb7297fe..aff6139c08 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var storyboard = new Storyboard(); var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Commands.AddAlpha(0, duration, 1, 0, Easing.None); + sprite.Commands.AddAlpha(Easing.None, 0, duration, 1, 0); storyboard.GetLayer("Background").Add(sprite); return storyboard; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 7cd2e2fdaa..2b17f91e68 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -424,7 +424,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestIntroStoryboardElement() => testLeadIn(b => { var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.Commands.AddAlpha(-2000, 0, 0, 1, Easing.None); + sprite.Commands.AddAlpha(Easing.None, -2000, 0, 0, 1); b.Storyboard.GetLayer("Background").Add(sprite); }); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 739e81a2fe..195d59d0eb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -204,7 +204,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentCommandsGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddAlpha(easing, startTime, endTime, startValue, endValue); break; } @@ -212,7 +212,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentCommandsGroup?.AddScale(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddScale(easing, startTime, endTime, startValue, endValue); break; } @@ -222,7 +222,7 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - currentCommandsGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing); + currentCommandsGroup?.AddVectorScale(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); break; } @@ -230,7 +230,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentCommandsGroup?.AddRotation(startTime, endTime, float.RadiansToDegrees(startValue), float.RadiansToDegrees(endValue), easing); + currentCommandsGroup?.AddRotation(easing, startTime, endTime, float.RadiansToDegrees(startValue), float.RadiansToDegrees(endValue)); break; } @@ -240,8 +240,8 @@ namespace osu.Game.Beatmaps.Formats float startY = Parsing.ParseFloat(split[5]); float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; - currentCommandsGroup?.AddX(startTime, endTime, startX, endX, easing); - currentCommandsGroup?.AddY(startTime, endTime, startY, endY, easing); + currentCommandsGroup?.AddX(easing, startTime, endTime, startX, endX); + currentCommandsGroup?.AddY(easing, startTime, endTime, startY, endY); break; } @@ -249,7 +249,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentCommandsGroup?.AddX(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddX(easing, startTime, endTime, startValue, endValue); break; } @@ -257,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats { float startValue = Parsing.ParseFloat(split[4]); float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; - currentCommandsGroup?.AddY(startTime, endTime, startValue, endValue, easing); + currentCommandsGroup?.AddY(easing, startTime, endTime, startValue, endValue); break; } @@ -269,9 +269,9 @@ namespace osu.Game.Beatmaps.Formats float endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed; float endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen; float endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue; - currentCommandsGroup?.AddColour(startTime, endTime, + currentCommandsGroup?.AddColour(easing, startTime, endTime, new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1), easing); + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); break; } @@ -282,16 +282,16 @@ namespace osu.Game.Beatmaps.Formats switch (type) { case "A": - currentCommandsGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive, - startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit, easing); + currentCommandsGroup?.AddBlendingParameters(easing, startTime, endTime, BlendingParameters.Additive, + startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit); break; case "H": - currentCommandsGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing); + currentCommandsGroup?.AddFlipH(easing, startTime, endTime, true, startTime == endTime); break; case "V": - currentCommandsGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing); + currentCommandsGroup?.AddFlipV(easing, startTime, endTime, true, startTime == endTime); break; } diff --git a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs index f4a90b372a..1c17da7592 100644 --- a/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardAlphaCommand.cs @@ -8,8 +8,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardAlphaCommand : StoryboardCommand { - public StoryboardAlphaCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardAlphaCommand(Easing easing, double startTime, double endTime, float startValue, float endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs index 3e2e8bb0e8..9ac6613708 100644 --- a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs @@ -8,8 +8,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardBlendingParametersCommand : StoryboardCommand { - public StoryboardBlendingParametersCommand(double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardBlendingParametersCommand(Easing easing, double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs index 66390eb305..da8a20647c 100644 --- a/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardColourCommand.cs @@ -9,8 +9,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardColourCommand : StoryboardCommand { - public StoryboardColourCommand(double startTime, double endTime, Color4 startValue, Color4 endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardColourCommand(Easing easing, double startTime, double endTime, Color4 startValue, Color4 endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardCommand.cs b/osu.Game/Storyboards/Commands/StoryboardCommand.cs index 4f2f0f04a2..60c28e7833 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommand.cs @@ -19,7 +19,7 @@ namespace osu.Game.Storyboards.Commands public double Duration => EndTime - StartTime; - protected StoryboardCommand(double startTime, double endTime, T startValue, T endValue, Easing easing) + protected StoryboardCommand(Easing easing, double startTime, double endTime, T startValue, T endValue) { if (endTime < startTime) endTime = startTime; diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs index fb847d2e44..40dd8f78e6 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -45,35 +45,35 @@ namespace osu.Game.Storyboards.Commands [JsonIgnore] public bool HasCommands { get; private set; } - public void AddX(double startTime, double endTime, float startValue, float endValue, Easing easing) - => AddCommand(X, new StoryboardXCommand(startTime, endTime, startValue, endValue, easing)); + public void AddX(Easing easing, double startTime, double endTime, float startValue, float endValue) + => AddCommand(X, new StoryboardXCommand(easing, startTime, endTime, startValue, endValue)); - public void AddY(double startTime, double endTime, float startValue, float endValue, Easing easing) - => AddCommand(Y, new StoryboardYCommand(startTime, endTime, startValue, endValue, easing)); + public void AddY(Easing easing, double startTime, double endTime, float startValue, float endValue) + => AddCommand(Y, new StoryboardYCommand(easing, startTime, endTime, startValue, endValue)); - public void AddScale(double startTime, double endTime, float startValue, float endValue, Easing easing) - => AddCommand(Scale, new StoryboardScaleCommand(startTime, endTime, startValue, endValue, easing)); + public void AddScale(Easing easing, double startTime, double endTime, float startValue, float endValue) + => AddCommand(Scale, new StoryboardScaleCommand(easing, startTime, endTime, startValue, endValue)); - public void AddVectorScale(double startTime, double endTime, Vector2 startValue, Vector2 endValue, Easing easing) - => AddCommand(VectorScale, new StoryboardVectorScaleCommand(startTime, endTime, startValue, endValue, easing)); + public void AddVectorScale(Easing easing, double startTime, double endTime, Vector2 startValue, Vector2 endValue) + => AddCommand(VectorScale, new StoryboardVectorScaleCommand(easing, startTime, endTime, startValue, endValue)); - public void AddRotation(double startTime, double endTime, float startValue, float endValue, Easing easing) - => AddCommand(Rotation, new StoryboardRotationCommand(startTime, endTime, startValue, endValue, easing)); + public void AddRotation(Easing easing, double startTime, double endTime, float startValue, float endValue) + => AddCommand(Rotation, new StoryboardRotationCommand(easing, startTime, endTime, startValue, endValue)); - public void AddColour(double startTime, double endTime, Color4 startValue, Color4 endValue, Easing easing) - => AddCommand(Colour, new StoryboardColourCommand(startTime, endTime, startValue, endValue, easing)); + public void AddColour(Easing easing, double startTime, double endTime, Color4 startValue, Color4 endValue) + => AddCommand(Colour, new StoryboardColourCommand(easing, startTime, endTime, startValue, endValue)); - public void AddAlpha(double startTime, double endTime, float startValue, float endValue, Easing easing) - => AddCommand(Alpha, new StoryboardAlphaCommand(startTime, endTime, startValue, endValue, easing)); + public void AddAlpha(Easing easing, double startTime, double endTime, float startValue, float endValue) + => AddCommand(Alpha, new StoryboardAlphaCommand(easing, startTime, endTime, startValue, endValue)); - public void AddBlendingParameters(double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue, Easing easing) - => AddCommand(BlendingParameters, new StoryboardBlendingParametersCommand(startTime, endTime, startValue, endValue, easing)); + public void AddBlendingParameters(Easing easing, double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue) + => AddCommand(BlendingParameters, new StoryboardBlendingParametersCommand(easing, startTime, endTime, startValue, endValue)); - public void AddFlipH(double startTime, double endTime, bool startValue, bool endValue, Easing easing) - => AddCommand(FlipH, new StoryboardFlipHCommand(startTime, endTime, startValue, endValue, easing)); + public void AddFlipH(Easing easing, double startTime, double endTime, bool startValue, bool endValue) + => AddCommand(FlipH, new StoryboardFlipHCommand(easing, startTime, endTime, startValue, endValue)); - public void AddFlipV(double startTime, double endTime, bool startValue, bool endValue, Easing easing) - => AddCommand(FlipV, new StoryboardFlipVCommand(startTime, endTime, startValue, endValue, easing)); + public void AddFlipV(Easing easing, double startTime, double endTime, bool startValue, bool endValue) + => AddCommand(FlipV, new StoryboardFlipVCommand(easing, startTime, endTime, startValue, endValue)); /// /// Adds the given storyboard to the target . diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs index 26aef23226..fa07ff6645 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs @@ -9,8 +9,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardFlipHCommand : StoryboardCommand { - public StoryboardFlipHCommand(double startTime, double endTime, bool startValue, bool endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardFlipHCommand(Easing easing, double startTime, double endTime, bool startValue, bool endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs index 88423da2af..fa6a170c25 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs @@ -9,8 +9,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardFlipVCommand : StoryboardCommand { - public StoryboardFlipVCommand(double startTime, double endTime, bool startValue, bool endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardFlipVCommand(Easing easing, double startTime, double endTime, bool startValue, bool endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs index e97de84ab7..a886998679 100644 --- a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs @@ -43,7 +43,7 @@ namespace osu.Game.Storyboards.Commands // In an ideal world, we would multiply the command duration by TotalIterations in command end time. // Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro // sequences for minutes or hours. - : base(loopingGroup.loopStartTime + command.StartTime, loopingGroup.loopStartTime + command.EndTime, command.StartValue, command.EndValue, command.Easing) + : base(command.Easing, loopingGroup.loopStartTime + command.StartTime, loopingGroup.loopStartTime + command.EndTime, command.StartValue, command.EndValue) { this.command = command; this.loopingGroup = loopingGroup; diff --git a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs index 4347dc9d77..7e097fce25 100644 --- a/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardRotationCommand.cs @@ -8,8 +8,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardRotationCommand : StoryboardCommand { - public StoryboardRotationCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardRotationCommand(Easing easing, double startTime, double endTime, float startValue, float endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs index b0f33fd6b8..832533af5e 100644 --- a/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardScaleCommand.cs @@ -9,8 +9,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardScaleCommand : StoryboardCommand { - public StoryboardScaleCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardScaleCommand(Easing easing, double startTime, double endTime, float startValue, float endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs index 5d3fef5948..06983a1590 100644 --- a/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardVectorScaleCommand.cs @@ -10,8 +10,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardVectorScaleCommand : StoryboardCommand { - public StoryboardVectorScaleCommand(double startTime, double endTime, Vector2 startValue, Vector2 endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardVectorScaleCommand(Easing easing, double startTime, double endTime, Vector2 startValue, Vector2 endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs index 7df9a75768..d52e9c8a05 100644 --- a/osu.Game/Storyboards/Commands/StoryboardXCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardXCommand.cs @@ -8,8 +8,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardXCommand : StoryboardCommand { - public StoryboardXCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardXCommand(Easing easing, double startTime, double endTime, float startValue, float endValue) + : base(easing, startTime, endTime, startValue, endValue) { } diff --git a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs index d7dc32a0f3..90dfe4d995 100644 --- a/osu.Game/Storyboards/Commands/StoryboardYCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardYCommand.cs @@ -8,8 +8,8 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardYCommand : StoryboardCommand { - public StoryboardYCommand(double startTime, double endTime, float startValue, float endValue, Easing easing) - : base(startTime, endTime, startValue, endValue, easing) + public StoryboardYCommand(Easing easing, double startTime, double endTime, float startValue, float endValue) + : base(easing, startTime, endTime, startValue, endValue) { } From 0efa12a86a5b3f19251e90b65f80f523a9ded5bc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 21:52:56 +0300 Subject: [PATCH 069/581] Fix parameter commands applying initial value before start time --- osu.Game/Storyboards/Commands/IStoryboardCommand.cs | 3 +++ .../Commands/StoryboardBlendingParametersCommand.cs | 6 +++++- osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs | 6 +++++- osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs | 6 +++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs index 6efb19afe4..ea14f5fa40 100644 --- a/osu.Game/Storyboards/Commands/IStoryboardCommand.cs +++ b/osu.Game/Storyboards/Commands/IStoryboardCommand.cs @@ -28,6 +28,9 @@ namespace osu.Game.Storyboards.Commands /// /// Sets the value of the corresponding property in to the start value of this command. /// + /// + /// Parameter commands (e.g. / / ) only apply the start value if they have zero duration, i.e. take "permanent" effect regardless of time. + /// /// The target drawable. void ApplyInitialValue(TDrawable d) where TDrawable : Drawable, IFlippable, IVectorScalable; diff --git a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs index 9ac6613708..cf9cadf1a7 100644 --- a/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardBlendingParametersCommand.cs @@ -15,7 +15,11 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(Drawable.Blending); - public override void ApplyInitialValue(TDrawable d) => d.Blending = StartValue; + public override void ApplyInitialValue(TDrawable d) + { + if (StartTime == EndTime) + d.Blending = StartValue; + } public override TransformSequence ApplyTransforms(TDrawable d) => d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration) diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs index fa07ff6645..fbf7295f15 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipHCommand.cs @@ -16,7 +16,11 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(IFlippable.FlipH); - public override void ApplyInitialValue(TDrawable d) => d.FlipH = StartValue; + public override void ApplyInitialValue(TDrawable d) + { + if (StartTime == EndTime) + d.FlipH = StartValue; + } public override TransformSequence ApplyTransforms(TDrawable d) => d.TransformTo(nameof(IFlippable.FlipH), StartValue).Delay(Duration) diff --git a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs index fa6a170c25..136bd52f1f 100644 --- a/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs +++ b/osu.Game/Storyboards/Commands/StoryboardFlipVCommand.cs @@ -16,7 +16,11 @@ namespace osu.Game.Storyboards.Commands public override string PropertyName => nameof(IFlippable.FlipV); - public override void ApplyInitialValue(TDrawable d) => d.FlipV = StartValue; + public override void ApplyInitialValue(TDrawable d) + { + if (StartTime == EndTime) + d.FlipV = StartValue; + } public override TransformSequence ApplyTransforms(TDrawable d) => d.TransformTo(nameof(IFlippable.FlipV), StartValue).Delay(Duration) From 6861d9a302f2995ac05ff717e8911d9eabe3e25b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 22:28:13 +0300 Subject: [PATCH 070/581] Expose storyboard command lists as read-only and remove unnecessary memory footprint Mutation should be done only with the methods exposed by `StoryboardCommandGroup`. --- .../Commands/StoryboardCommandGroup.cs | 80 +++++++++++++------ 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs index 40dd8f78e6..0925231412 100644 --- a/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardCommandGroup.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Lists; @@ -12,20 +13,45 @@ namespace osu.Game.Storyboards.Commands { public class StoryboardCommandGroup { - public SortedList> X = new SortedList>(); - public SortedList> Y = new SortedList>(); - public SortedList> Scale = new SortedList>(); - public SortedList> VectorScale = new SortedList>(); - public SortedList> Rotation = new SortedList>(); - public SortedList> Colour = new SortedList>(); - public SortedList> Alpha = new SortedList>(); - public SortedList> BlendingParameters = new SortedList>(); - public SortedList> FlipH = new SortedList>(); - public SortedList> FlipV = new SortedList>(); + private readonly SortedList> x = new SortedList>(); - public IReadOnlyList AllCommands => allCommands; + public IReadOnlyList> X => x; - private readonly List allCommands = new List(); + private readonly SortedList> y = new SortedList>(); + + public IReadOnlyList> Y => y; + + private readonly SortedList> scale = new SortedList>(); + + public IReadOnlyList> Scale => scale; + + private readonly SortedList> vectorScale = new SortedList>(); + + public IReadOnlyList> VectorScale => vectorScale; + + private readonly SortedList> rotation = new SortedList>(); + + public IReadOnlyList> Rotation => rotation; + + private readonly SortedList> colour = new SortedList>(); + + public IReadOnlyList> Colour => colour; + + private readonly SortedList> alpha = new SortedList>(); + + public IReadOnlyList> Alpha => alpha; + + private readonly SortedList> blendingParameters = new SortedList>(); + + public IReadOnlyList> BlendingParameters => blendingParameters; + + private readonly SortedList> flipH = new SortedList>(); + + public IReadOnlyList> FlipH => flipH; + + private readonly SortedList> flipV = new SortedList>(); + + public IReadOnlyList> FlipV => flipV; /// /// Returns the earliest start time of the commands added to this group. @@ -45,35 +71,44 @@ namespace osu.Game.Storyboards.Commands [JsonIgnore] public bool HasCommands { get; private set; } + private readonly IReadOnlyList[] lists; + + public IEnumerable AllCommands => lists.SelectMany(g => g); + + public StoryboardCommandGroup() + { + lists = new IReadOnlyList[] { X, Y, Scale, VectorScale, Rotation, Colour, Alpha, BlendingParameters, FlipH, FlipV }; + } + public void AddX(Easing easing, double startTime, double endTime, float startValue, float endValue) - => AddCommand(X, new StoryboardXCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(x, new StoryboardXCommand(easing, startTime, endTime, startValue, endValue)); public void AddY(Easing easing, double startTime, double endTime, float startValue, float endValue) - => AddCommand(Y, new StoryboardYCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(y, new StoryboardYCommand(easing, startTime, endTime, startValue, endValue)); public void AddScale(Easing easing, double startTime, double endTime, float startValue, float endValue) - => AddCommand(Scale, new StoryboardScaleCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(scale, new StoryboardScaleCommand(easing, startTime, endTime, startValue, endValue)); public void AddVectorScale(Easing easing, double startTime, double endTime, Vector2 startValue, Vector2 endValue) - => AddCommand(VectorScale, new StoryboardVectorScaleCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(vectorScale, new StoryboardVectorScaleCommand(easing, startTime, endTime, startValue, endValue)); public void AddRotation(Easing easing, double startTime, double endTime, float startValue, float endValue) - => AddCommand(Rotation, new StoryboardRotationCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(rotation, new StoryboardRotationCommand(easing, startTime, endTime, startValue, endValue)); public void AddColour(Easing easing, double startTime, double endTime, Color4 startValue, Color4 endValue) - => AddCommand(Colour, new StoryboardColourCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(colour, new StoryboardColourCommand(easing, startTime, endTime, startValue, endValue)); public void AddAlpha(Easing easing, double startTime, double endTime, float startValue, float endValue) - => AddCommand(Alpha, new StoryboardAlphaCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(alpha, new StoryboardAlphaCommand(easing, startTime, endTime, startValue, endValue)); public void AddBlendingParameters(Easing easing, double startTime, double endTime, BlendingParameters startValue, BlendingParameters endValue) - => AddCommand(BlendingParameters, new StoryboardBlendingParametersCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(blendingParameters, new StoryboardBlendingParametersCommand(easing, startTime, endTime, startValue, endValue)); public void AddFlipH(Easing easing, double startTime, double endTime, bool startValue, bool endValue) - => AddCommand(FlipH, new StoryboardFlipHCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(flipH, new StoryboardFlipHCommand(easing, startTime, endTime, startValue, endValue)); public void AddFlipV(Easing easing, double startTime, double endTime, bool startValue, bool endValue) - => AddCommand(FlipV, new StoryboardFlipVCommand(easing, startTime, endTime, startValue, endValue)); + => AddCommand(flipV, new StoryboardFlipVCommand(easing, startTime, endTime, startValue, endValue)); /// /// Adds the given storyboard to the target . @@ -83,7 +118,6 @@ namespace osu.Game.Storyboards.Commands protected virtual void AddCommand(ICollection> list, StoryboardCommand command) { list.Add(command); - allCommands.Add(command); HasCommands = true; if (command.StartTime < StartTime) From 1942d46a38fac400e28dbe95a3d5a2d6c0a95cc2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 22:37:27 +0300 Subject: [PATCH 071/581] Remove leftover debugging code --- osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index 507a51aca4..da4b5c641d 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -102,9 +102,6 @@ namespace osu.Game.Storyboards.Drawables else Texture = textureStore.Get(Sprite.Path); - if (Sprite.Path == "SB/textbox.png") - Debugger.Break(); - Sprite.ApplyTransforms(this); } From 8c92bb0595f6528c86c39a0851fd7e6edd8be5f6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 23:09:16 +0300 Subject: [PATCH 072/581] Remove unused using directive --- osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index da4b5c641d..ec875219b6 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; From 9f71eac1dbe26df48cdcdaff181a0ca6be71f219 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 8 Mar 2024 23:09:50 +0300 Subject: [PATCH 073/581] Remove extra end line --- osu.Game/Storyboards/StoryboardSprite.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 944d77e745..80a76cd831 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -195,4 +195,3 @@ namespace osu.Game.Storyboards public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; } } - From 82048df9f16607c8b9887ddf91cf880fdb5927c3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 9 Mar 2024 04:42:17 +0300 Subject: [PATCH 074/581] Add basic test scene for asserting storyboard commands behaviour Pending actual test coverage. --- .../Gameplay/TestSceneStoryboardCommands.cs | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs new file mode 100644 index 0000000000..1893182b32 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs @@ -0,0 +1,166 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.IO.Stores; +using osu.Framework.Timing; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osu.Game.Storyboards; +using osu.Game.Storyboards.Drawables; +using osu.Game.Tests.Resources; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public partial class TestSceneStoryboardCommands : OsuTestScene + { + [Cached(typeof(Storyboard))] + private TestStoryboard storyboard { get; set; } = new TestStoryboard + { + UseSkinSprites = false, + AlwaysProvideTexture = true, + }; + + private readonly ManualClock manualClock = new ManualClock { Rate = 1, IsRunning = true }; + private bool forward; + + private const string lookup_name = "hitcircleoverlay"; + private const double clock_limit = 2500; + + protected override Container Content => content; + + private Container content = null!; + private SpriteText timelineText = null!; + private Box timelineMarker = null!; + + [BackgroundDependencyLoader] + private void load() + { + base.Content.Children = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.Both, + }, + timelineText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Bottom = 60 }, + }, + timelineMarker = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.X, + Size = new Vector2(2, 50), + }, + }; + } + + [SetUp] + public void SetUp() + { + manualClock.CurrentTime = 0; + forward = true; + } + + [Test] + public void TestLoop() + { + AddStep("create sprite", () => Child = createSprite(s => + { + var loop = s.AddLoopingGroup(600, 10); + loop.AddY(Easing.OutBounce, 0, 500, 100, 240); + loop.AddY(Easing.OutQuint, 700, 1000, 240, 100); + })); + } + + protected override void Update() + { + base.Update(); + + if (manualClock.CurrentTime > clock_limit || manualClock.CurrentTime < 0) + forward = !forward; + + manualClock.CurrentTime += Time.Elapsed * (forward ? 1 : -1); + timelineText.Text = $"Time: {manualClock.CurrentTime:0}ms"; + timelineMarker.X = (float)(manualClock.CurrentTime / clock_limit); + } + + private DrawableStoryboard createSprite(Action? addCommands = null) + { + var layer = storyboard.GetLayer("Background"); + + var sprite = new StoryboardSprite(lookup_name, Anchor.Centre, new Vector2(320, 240)); + sprite.Commands.AddScale(Easing.None, 0, clock_limit, 0.5f, 0.5f); + sprite.Commands.AddAlpha(Easing.None, 0, clock_limit, 1, 1); + addCommands?.Invoke(sprite); + + layer.Elements.Clear(); + layer.Add(sprite); + + return storyboard.CreateDrawable().With(c => c.Clock = new FramedClock(manualClock)); + } + + private partial class TestStoryboard : Storyboard + { + public override DrawableStoryboard CreateDrawable(IReadOnlyList? mods = null) + { + return new TestDrawableStoryboard(this, mods); + } + + public bool AlwaysProvideTexture { get; set; } + + public override string GetStoragePathFromStoryboardPath(string path) => AlwaysProvideTexture ? path : string.Empty; + + private partial class TestDrawableStoryboard : DrawableStoryboard + { + private readonly bool alwaysProvideTexture; + + public TestDrawableStoryboard(TestStoryboard storyboard, IReadOnlyList? mods) + : base(storyboard, mods) + { + alwaysProvideTexture = storyboard.AlwaysProvideTexture; + } + + protected override IResourceStore CreateResourceLookupStore() => alwaysProvideTexture + ? new AlwaysReturnsTextureStore() + : new ResourceStore(); + + internal class AlwaysReturnsTextureStore : IResourceStore + { + private const string test_image = "Resources/Textures/test-image.png"; + + private readonly DllResourceStore store; + + public AlwaysReturnsTextureStore() + { + store = TestResources.GetStore(); + } + + public void Dispose() => store.Dispose(); + + public byte[] Get(string name) => store.Get(test_image); + + public Task GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(test_image, cancellationToken); + + public Stream GetStream(string name) => store.GetStream(test_image); + + public IEnumerable GetAvailableResources() => store.GetAvailableResources(); + } + } + } + } +} From 8a1c5a754763cb8cc25963ab0e5b3439683a357a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 10 Mar 2024 07:23:22 +0300 Subject: [PATCH 075/581] Adjust time values --- .../Visual/Gameplay/TestSceneStoryboardCommands.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs index 1893182b32..11c07824d3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs @@ -81,9 +81,9 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create sprite", () => Child = createSprite(s => { - var loop = s.AddLoopingGroup(600, 10); - loop.AddY(Easing.OutBounce, 0, 500, 100, 240); - loop.AddY(Easing.OutQuint, 700, 1000, 240, 100); + var loop = s.AddLoopingGroup(500, 10); + loop.AddY(Easing.OutBounce, 0, 600, 100, 240); + loop.AddY(Easing.OutQuint, 800, 1200, 240, 100); })); } From d039b565621790c8df3b5eaf0243deaa0cd9d5bb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 10 Mar 2024 07:26:27 +0300 Subject: [PATCH 076/581] Add test case for running with high number of loops --- .../Visual/Gameplay/TestSceneStoryboardCommands.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs index 11c07824d3..1392cc3a21 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs @@ -87,6 +87,17 @@ namespace osu.Game.Tests.Visual.Gameplay })); } + [Test] + public void TestLoopManyTimes() + { + AddStep("create sprite", () => Child = createSprite(s => + { + var loop = s.AddLoopingGroup(500, 10000); + loop.AddY(Easing.OutBounce, 0, 60, 100, 240); + loop.AddY(Easing.OutQuint, 80, 120, 240, 100); + })); + } + protected override void Update() { base.Update(); From 99b06102b1bb67cd13c2e582c9796212d31c65d1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 10 Mar 2024 09:01:31 +0300 Subject: [PATCH 077/581] Add enough test coverage --- .../Gameplay/TestSceneStoryboardCommands.cs | 115 +++++++++++++++--- 1 file changed, 101 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs index 1392cc3a21..4af3d23463 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardCommands.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -13,6 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; @@ -33,7 +35,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; private readonly ManualClock manualClock = new ManualClock { Rate = 1, IsRunning = true }; - private bool forward; + private int clockDirection; private const string lookup_name = "hitcircleoverlay"; private const double clock_limit = 2500; @@ -69,28 +71,75 @@ namespace osu.Game.Tests.Visual.Gameplay }; } - [SetUp] - public void SetUp() + [SetUpSteps] + public void SetUpSteps() { - manualClock.CurrentTime = 0; - forward = true; + AddStep("start clock", () => clockDirection = 1); + AddStep("pause clock", () => clockDirection = 0); + AddStep("set clock = 0", () => manualClock.CurrentTime = 0); } [Test] - public void TestLoop() + public void TestNormalCommandPlayback() { - AddStep("create sprite", () => Child = createSprite(s => + AddStep("create storyboard", () => Child = createStoryboard(s => { - var loop = s.AddLoopingGroup(500, 10); - loop.AddY(Easing.OutBounce, 0, 600, 100, 240); - loop.AddY(Easing.OutQuint, 800, 1200, 240, 100); + s.Commands.AddY(Easing.OutBounce, 500, 900, 100, 240); + s.Commands.AddY(Easing.OutQuint, 1100, 1500, 240, 100); })); + + assert(0, 100); + assert(500, 100); + assert(1000, 240); + assert(1500, 100); + assert(clock_limit, 100); + assert(1500, 100); + assert(1000, 240); + assert(500, 100); + assert(0, 100); + + void assert(double time, double y) + { + AddStep($"set clock = {time}", () => manualClock.CurrentTime = time); + AddAssert($"sprite y = {y} at t = {time}", () => this.ChildrenOfType().Single().Y == y); + } + } + + [Test] + public void TestLoopingCommandsPlayback() + { + AddStep("create storyboard", () => Child = createStoryboard(s => + { + var loop = s.AddLoopingGroup(250, 1); + loop.AddY(Easing.OutBounce, 0, 400, 100, 240); + loop.AddY(Easing.OutQuint, 600, 1000, 240, 100); + })); + + assert(0, 100); + assert(250, 100); + assert(850, 240); + assert(1250, 100); + assert(1850, 240); + assert(2250, 100); + assert(clock_limit, 100); + assert(2250, 100); + assert(1850, 240); + assert(1250, 100); + assert(850, 240); + assert(250, 100); + assert(0, 100); + + void assert(double time, double y) + { + AddStep($"set clock = {time}", () => manualClock.CurrentTime = time); + AddAssert($"sprite y = {y} at t = {time}", () => this.ChildrenOfType().Single().Y == y); + } } [Test] public void TestLoopManyTimes() { - AddStep("create sprite", () => Child = createSprite(s => + AddStep("create storyboard", () => Child = createStoryboard(s => { var loop = s.AddLoopingGroup(500, 10000); loop.AddY(Easing.OutBounce, 0, 60, 100, 240); @@ -98,19 +147,57 @@ namespace osu.Game.Tests.Visual.Gameplay })); } + [Test] + public void TestParameterTemporaryEffect() + { + AddStep("create storyboard", () => Child = createStoryboard(s => + { + s.Commands.AddFlipV(Easing.None, 1000, 1500, true, false); + })); + + AddAssert("sprite not flipped at t = 0", () => !this.ChildrenOfType().Single().FlipV); + + AddStep("set clock = 1250", () => manualClock.CurrentTime = 1250); + AddAssert("sprite flipped at t = 1250", () => this.ChildrenOfType().Single().FlipV); + + AddStep("set clock = 2000", () => manualClock.CurrentTime = 2000); + AddAssert("sprite not flipped at t = 2000", () => !this.ChildrenOfType().Single().FlipV); + + AddStep("resume clock", () => clockDirection = 1); + } + + [Test] + public void TestParameterPermanentEffect() + { + AddStep("create storyboard", () => Child = createStoryboard(s => + { + s.Commands.AddFlipV(Easing.None, 1000, 1000, true, true); + })); + + AddAssert("sprite flipped at t = 0", () => this.ChildrenOfType().Single().FlipV); + + AddStep("set clock = 1250", () => manualClock.CurrentTime = 1250); + AddAssert("sprite flipped at t = 1250", () => this.ChildrenOfType().Single().FlipV); + + AddStep("set clock = 2000", () => manualClock.CurrentTime = 2000); + AddAssert("sprite flipped at t = 2000", () => this.ChildrenOfType().Single().FlipV); + + AddStep("resume clock", () => clockDirection = 1); + } + protected override void Update() { base.Update(); if (manualClock.CurrentTime > clock_limit || manualClock.CurrentTime < 0) - forward = !forward; + clockDirection = -clockDirection; - manualClock.CurrentTime += Time.Elapsed * (forward ? 1 : -1); + manualClock.CurrentTime += Time.Elapsed * clockDirection; timelineText.Text = $"Time: {manualClock.CurrentTime:0}ms"; timelineMarker.X = (float)(manualClock.CurrentTime / clock_limit); } - private DrawableStoryboard createSprite(Action? addCommands = null) + private DrawableStoryboard createStoryboard(Action? addCommands = null) { var layer = storyboard.GetLayer("Background"); From 0a6960296ec1947594b06623cfd925433e09d380 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Sat, 16 Mar 2024 21:32:55 +0200 Subject: [PATCH 078/581] feat: Support filtering for multiple statuses --- .../Filtering/FilterQueryParserTest.cs | 22 ++++++++++---- osu.Game/Screens/Select/FilterCriteria.cs | 17 ++++++++++- osu.Game/Screens/Select/FilterQueryParser.cs | 29 ++++++++++++++++++- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index bf888348ee..c109e5bad2 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -256,8 +256,9 @@ namespace osu.Game.Tests.NonVisual.Filtering const string query = "status=r"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min); - Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max); + Assert.IsNotNull(filterCriteria.OnlineStatus.Values); + Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values!); + Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); } [Test] @@ -268,10 +269,19 @@ namespace osu.Game.Tests.NonVisual.Filtering FilterQueryParser.ApplyQueries(filterCriteria, query); Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim()); Assert.AreEqual(4, filterCriteria.SearchTerms.Length); - Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min); - Assert.IsTrue(filterCriteria.OnlineStatus.IsLowerInclusive); - Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max); - Assert.IsTrue(filterCriteria.OnlineStatus.IsUpperInclusive); + Assert.IsNotNull(filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); + } + + [Test] + public void TestApplyStatusMatches() + { + const string query = "status=ranked status=loved"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.IsNotNull(filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Loved, filterCriteria.OnlineStatus.Values); } [TestCase("creator")] diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 46083f7c88..dd6b602ade 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select public OptionalRange Length; public OptionalRange BPM; public OptionalRange BeatDivisor; - public OptionalRange OnlineStatus; + public OptionalArray OnlineStatus; public OptionalRange LastPlayed; public OptionalTextFilter Creator; public OptionalTextFilter Artist; @@ -114,6 +114,21 @@ namespace osu.Game.Screens.Select public IRulesetFilterCriteria? RulesetCriteria { get; set; } + public struct OptionalArray : IEquatable> + where T : struct + { + public bool HasFilter => Values?.Length > 0; + + public bool IsInRange(T value) + { + return Values?.Contains(value) ?? false; + } + + public T[]? Values; + + public bool Equals(OptionalArray other) => Values?.SequenceEqual(other.Values ?? Array.Empty()) ?? false; + } + public struct OptionalRange : IEquatable> where T : struct { diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 2c4077dacf..208e7f559e 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); case "status": - return TryUpdateCriteriaRange(ref criteria.OnlineStatus, op, value, tryParseEnum); + return TryUpdateArrayRange(ref criteria.OnlineStatus, op, value, tryParseEnum); case "creator": case "author": @@ -300,6 +300,33 @@ namespace osu.Game.Screens.Select where T : struct => parseFunction.Invoke(val, out var converted) && tryUpdateCriteriaRange(ref range, op, converted); + /// + /// Attempts to parse a keyword filter of type , + /// from the specified and . + /// If can be parsed into using , the function returns true + /// and the resulting range constraint is stored into the 's expected values. + /// + /// The to store the parsed data into, if successful. + /// The operator for the keyword filter. Currently, only can be used. + /// The value of the keyword filter. + /// Function used to determine if can be converted to type . + public static bool TryUpdateArrayRange(ref FilterCriteria.OptionalArray range, Operator op, string val, TryParseFunction parseFunction) + where T : struct + => parseFunction.Invoke(val, out var converted) && tryUpdateArrayRange(ref range, op, converted); + + private static bool tryUpdateArrayRange(ref FilterCriteria.OptionalArray range, Operator op, T value) + where T : struct + { + if (op != Operator.Equal) + return false; + + range.Values ??= Array.Empty(); + + range.Values = range.Values.Append(value).ToArray(); + + return true; + } + private static bool tryUpdateCriteriaRange(ref FilterCriteria.OptionalRange range, Operator op, T value) where T : struct { From e1c1609271bee3b21c38383596f20f31c2a3573e Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Sat, 16 Mar 2024 21:48:44 +0200 Subject: [PATCH 079/581] chore: correct equal logic --- osu.Game/Screens/Select/FilterCriteria.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index dd6b602ade..6cec7a387d 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -126,7 +126,13 @@ namespace osu.Game.Screens.Select public T[]? Values; - public bool Equals(OptionalArray other) => Values?.SequenceEqual(other.Values ?? Array.Empty()) ?? false; + public bool Equals(OptionalArray other) + { + if (Values is null && other.Values is null) + return true; + + return Values?.SequenceEqual(other.Values ?? Array.Empty()) ?? false; + } } public struct OptionalRange : IEquatable> From d0678bfbee7952e2b82c6d25bd2abc5952937421 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Mon, 18 Mar 2024 15:30:43 +0200 Subject: [PATCH 080/581] chore: requested changes --- .../Filtering/FilterQueryParserTest.cs | 20 ++++++-- osu.Game/Screens/Select/FilterCriteria.cs | 19 +++---- osu.Game/Screens/Select/FilterQueryParser.cs | 50 +++++++++++++++---- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index c109e5bad2..98d3286409 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -256,8 +256,7 @@ namespace osu.Game.Tests.NonVisual.Filtering const string query = "status=r"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.IsNotNull(filterCriteria.OnlineStatus.Values); - Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values!); + Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); } @@ -269,7 +268,7 @@ namespace osu.Game.Tests.NonVisual.Filtering FilterQueryParser.ApplyQueries(filterCriteria, query); Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim()); Assert.AreEqual(4, filterCriteria.SearchTerms.Length); - Assert.IsNotNull(filterCriteria.OnlineStatus.Values); + Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); } @@ -279,11 +278,24 @@ namespace osu.Game.Tests.NonVisual.Filtering const string query = "status=ranked status=loved"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.IsNotNull(filterCriteria.OnlineStatus.Values); + Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); Assert.Contains(BeatmapOnlineStatus.Loved, filterCriteria.OnlineStatus.Values); } + [Test] + public void TestApplyRangeStatusMatches() + { + const string query = "status>=r"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Approved, filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Qualified, filterCriteria.OnlineStatus.Values); + Assert.Contains(BeatmapOnlineStatus.Loved, filterCriteria.OnlineStatus.Values); + } + [TestCase("creator")] [TestCase("author")] [TestCase("mapper")] diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 6cec7a387d..1d155574cf 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select public OptionalRange Length; public OptionalRange BPM; public OptionalRange BeatDivisor; - public OptionalArray OnlineStatus; + public OptionalSet OnlineStatus = new OptionalSet(); public OptionalRange LastPlayed; public OptionalTextFilter Creator; public OptionalTextFilter Artist; @@ -114,24 +114,25 @@ namespace osu.Game.Screens.Select public IRulesetFilterCriteria? RulesetCriteria { get; set; } - public struct OptionalArray : IEquatable> + public struct OptionalSet : IEquatable> where T : struct { - public bool HasFilter => Values?.Length > 0; + public bool HasFilter => Values.Count > 0; public bool IsInRange(T value) { - return Values?.Contains(value) ?? false; + return Values.Contains(value); } - public T[]? Values; + public SortedSet Values = new SortedSet(); - public bool Equals(OptionalArray other) + public OptionalSet() { - if (Values is null && other.Values is null) - return true; + } - return Values?.SequenceEqual(other.Values ?? Array.Empty()) ?? false; + public bool Equals(OptionalSet other) + { + return Values.SetEquals(other.Values); } } diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 208e7f559e..fbb09b93bd 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); case "status": - return TryUpdateArrayRange(ref criteria.OnlineStatus, op, value, tryParseEnum); + return TryUpdateSetRange(ref criteria.OnlineStatus, op, value, tryParseEnum); case "creator": case "author": @@ -306,23 +306,55 @@ namespace osu.Game.Screens.Select /// If can be parsed into using , the function returns true /// and the resulting range constraint is stored into the 's expected values. /// - /// The to store the parsed data into, if successful. + /// The to store the parsed data into, if successful. /// The operator for the keyword filter. Currently, only can be used. /// The value of the keyword filter. /// Function used to determine if can be converted to type . - public static bool TryUpdateArrayRange(ref FilterCriteria.OptionalArray range, Operator op, string val, TryParseFunction parseFunction) + public static bool TryUpdateSetRange(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) where T : struct - => parseFunction.Invoke(val, out var converted) && tryUpdateArrayRange(ref range, op, converted); + => parseFunction.Invoke(val, out var converted) && tryUpdateSetRange(ref range, op, converted); - private static bool tryUpdateArrayRange(ref FilterCriteria.OptionalArray range, Operator op, T value) + private static bool tryUpdateSetRange(ref FilterCriteria.OptionalSet range, Operator op, T value) where T : struct { - if (op != Operator.Equal) - return false; + var enumValues = (T[])Enum.GetValues(typeof(T)); - range.Values ??= Array.Empty(); + foreach (var enumValue in enumValues) + { + switch (op) + { + case Operator.Less: + if (Comparer.Default.Compare(enumValue, value) < 0) + range.Values.Add(enumValue); - range.Values = range.Values.Append(value).ToArray(); + break; + + case Operator.LessOrEqual: + if (Comparer.Default.Compare(enumValue, value) <= 0) + range.Values.Add(enumValue); + + break; + + case Operator.Equal: + range.Values.Add(value); + break; + + case Operator.GreaterOrEqual: + if (Comparer.Default.Compare(enumValue, value) >= 0) + range.Values.Add(enumValue); + + break; + + case Operator.Greater: + if (Comparer.Default.Compare(enumValue, value) > 0) + range.Values.Add(enumValue); + + break; + + default: + return false; + } + } return true; } From 77119b2cdb1c4f2b743aa9a99eac1f48f74064d1 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Mon, 18 Mar 2024 16:44:42 +0200 Subject: [PATCH 081/581] chore: correct doc string --- osu.Game/Screens/Select/FilterQueryParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index fbb09b93bd..76d58963e7 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -307,7 +307,7 @@ namespace osu.Game.Screens.Select /// and the resulting range constraint is stored into the 's expected values. /// /// The to store the parsed data into, if successful. - /// The operator for the keyword filter. Currently, only can be used. + /// The operator for the keyword filter. /// The value of the keyword filter. /// Function used to determine if can be converted to type . public static bool TryUpdateSetRange(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) From a8ce6a0bba5c65ab12360191d7589365d0996133 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Mar 2024 02:43:34 +0300 Subject: [PATCH 082/581] Use `Slice` method instead of index range operators for readability --- .../Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 2b058d5e1f..283a59b7ed 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -303,16 +303,16 @@ namespace osu.Game.Rulesets.Objects.Legacy { int startIndex = segmentsBuffer[i].StartIndex; int endIndex = segmentsBuffer[i + 1].StartIndex; - controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints[startIndex..endIndex], pointsBuffer[endIndex])); + controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints.Slice(startIndex, endIndex - startIndex), pointsBuffer[endIndex])); } else { int startIndex = segmentsBuffer[i].StartIndex; - controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints[startIndex..], null)); + controlPoints.AddRange(convertPoints(segmentsBuffer[i].Type, allPoints.Slice(startIndex), null)); } } - return mergePointsLists(controlPoints); + return mergeControlPointsLists(controlPoints); } finally { @@ -402,7 +402,7 @@ namespace osu.Game.Rulesets.Objects.Legacy - (p1.X - p0.X) * (p2.Y - p0.Y)); } - private PathControlPoint[] mergePointsLists(List> controlPointList) + private PathControlPoint[] mergeControlPointsLists(List> controlPointList) { int totalCount = 0; From feaf59e15f701e1348122e17c4650420b8a581ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Mar 2024 18:19:06 +0100 Subject: [PATCH 083/581] Use `HashSet` instead of `SortedSet` No need for it to be sorted. --- .../NonVisual/Filtering/FilterQueryParserTest.cs | 16 ++++++++-------- osu.Game/Screens/Select/FilterCriteria.cs | 12 +++--------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index 98d3286409..bd706b5b4c 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -257,7 +257,7 @@ namespace osu.Game.Tests.NonVisual.Filtering var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); } [Test] @@ -269,7 +269,7 @@ namespace osu.Game.Tests.NonVisual.Filtering Assert.AreEqual("I want the pp", filterCriteria.SearchText.Trim()); Assert.AreEqual(4, filterCriteria.SearchTerms.Length); Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); } [Test] @@ -279,8 +279,8 @@ namespace osu.Game.Tests.NonVisual.Filtering var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Loved, filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Loved)); } [Test] @@ -290,10 +290,10 @@ namespace osu.Game.Tests.NonVisual.Filtering var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Approved, filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Qualified, filterCriteria.OnlineStatus.Values); - Assert.Contains(BeatmapOnlineStatus.Loved, filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Approved)); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Qualified)); + Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Loved)); } [TestCase("creator")] diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 1d155574cf..6750b07aba 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -119,21 +119,15 @@ namespace osu.Game.Screens.Select { public bool HasFilter => Values.Count > 0; - public bool IsInRange(T value) - { - return Values.Contains(value); - } + public bool IsInRange(T value) => Values.Contains(value); - public SortedSet Values = new SortedSet(); + public HashSet Values = new HashSet(); public OptionalSet() { } - public bool Equals(OptionalSet other) - { - return Values.SetEquals(other.Values); - } + public bool Equals(OptionalSet other) => Values.SetEquals(other.Values); } public struct OptionalRange : IEquatable> From 320373e3e0a50c5b94a0f5a68a108ce24b3da2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Mar 2024 18:28:13 +0100 Subject: [PATCH 084/581] Rename method to match convention better --- osu.Game/Screens/Select/FilterQueryParser.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 76d58963e7..194a288426 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); case "status": - return TryUpdateSetRange(ref criteria.OnlineStatus, op, value, tryParseEnum); + return TryUpdateCriteriaSet(ref criteria.OnlineStatus, op, value, tryParseEnum); case "creator": case "author": @@ -310,11 +310,11 @@ namespace osu.Game.Screens.Select /// The operator for the keyword filter. /// The value of the keyword filter. /// Function used to determine if can be converted to type . - public static bool TryUpdateSetRange(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) + public static bool TryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) where T : struct - => parseFunction.Invoke(val, out var converted) && tryUpdateSetRange(ref range, op, converted); + => parseFunction.Invoke(val, out var converted) && tryUpdateCriteriaSet(ref range, op, converted); - private static bool tryUpdateSetRange(ref FilterCriteria.OptionalSet range, Operator op, T value) + private static bool tryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, T value) where T : struct { var enumValues = (T[])Enum.GetValues(typeof(T)); From af3f7dcbbfb3e5b955f45e56815238eae71ccbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Mar 2024 18:31:07 +0100 Subject: [PATCH 085/581] Retouch update criteria method --- osu.Game/Screens/Select/FilterQueryParser.cs | 30 ++++++++------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 194a288426..32b533e18d 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -311,44 +311,38 @@ namespace osu.Game.Screens.Select /// The value of the keyword filter. /// Function used to determine if can be converted to type . public static bool TryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) - where T : struct + where T : struct, Enum => parseFunction.Invoke(val, out var converted) && tryUpdateCriteriaSet(ref range, op, converted); - private static bool tryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, T value) - where T : struct + private static bool tryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, T pivotValue) + where T : struct, Enum { - var enumValues = (T[])Enum.GetValues(typeof(T)); + var allDefinedValues = Enum.GetValues(); - foreach (var enumValue in enumValues) + foreach (var val in allDefinedValues) { + int compareResult = Comparer.Default.Compare(val, pivotValue); + switch (op) { case Operator.Less: - if (Comparer.Default.Compare(enumValue, value) < 0) - range.Values.Add(enumValue); - + if (compareResult < 0) range.Values.Add(val); break; case Operator.LessOrEqual: - if (Comparer.Default.Compare(enumValue, value) <= 0) - range.Values.Add(enumValue); - + if (compareResult <= 0) range.Values.Add(val); break; case Operator.Equal: - range.Values.Add(value); + if (compareResult == 0) range.Values.Add(val); break; case Operator.GreaterOrEqual: - if (Comparer.Default.Compare(enumValue, value) >= 0) - range.Values.Add(enumValue); - + if (compareResult >= 0) range.Values.Add(val); break; case Operator.Greater: - if (Comparer.Default.Compare(enumValue, value) > 0) - range.Values.Add(enumValue); - + if (compareResult > 0) range.Values.Add(val); break; default: From 4904c49c38b418f3a151c34c114be96983bb03b7 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 20 Mar 2024 09:31:58 -0300 Subject: [PATCH 086/581] Add abnormal difficulty settings checks --- .../CheckAbnormalDifficultySettingsTest.cs | 93 +++++++++++++++++++ osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 5 +- .../Checks/CheckAbnormalDifficultySettings.cs | 82 ++++++++++++++++ 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs b/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs new file mode 100644 index 0000000000..94305755c4 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs @@ -0,0 +1,93 @@ +// 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.Edit.Checks; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Edit; +using osu.Game.Tests.Beatmaps; +using System.Linq; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckAbnormalDifficultySettingsTest + { + private CheckAbnormalDifficultySettings check = null!; + + private IBeatmap beatmap = new Beatmap(); + + [SetUp] + public void Setup() + { + check = new CheckAbnormalDifficultySettings(); + beatmap.Difficulty = new(); + } + + [Test] + public void TestSettingsNormal() + { + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestAllSettingsMoreThanOneDecimal() + { + beatmap.Difficulty = new() + { + ApproachRate = 5.55f, + OverallDifficulty = 7.7777f, + CircleSize = 4.444f, + DrainRate = 1.1111111111f, + }; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(4)); + } + + [Test] + public void TestAllSettingsLessThanZero() + { + beatmap.Difficulty = new() + { + ApproachRate = -1, + OverallDifficulty = -20, + CircleSize = -11, + DrainRate = -34, + }; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(4)); + } + + [Test] + public void TestAllSettingsHigherThanTen() + { + beatmap.Difficulty = new() + { + ApproachRate = 14, + OverallDifficulty = 24, + CircleSize = 30, + DrainRate = 90, + }; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(4)); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index dcf5eb4da9..edabe941b7 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -41,7 +41,10 @@ namespace osu.Game.Rulesets.Edit new CheckPreviewTime(), // Events - new CheckBreaks() + new CheckBreaks(), + + // Settings + new CheckAbnormalDifficultySettings(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs new file mode 100644 index 0000000000..73049b323d --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs @@ -0,0 +1,82 @@ +// 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 osu.Game.Rulesets.Edit.Checks.Components; + + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckAbnormalDifficultySettings : ICheck + { + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Abnormal difficulty settings"); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateMoreThanOneDecimal(this), + new IssueTemplateOutOfRange(this), + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (hasMoreThanOneDecimalPlace(diff.ApproachRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); + + if (isOutOfRange(diff.ApproachRate)) + yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); + + + if (hasMoreThanOneDecimalPlace(diff.OverallDifficulty)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (isOutOfRange(diff.OverallDifficulty)) + yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + + + if (hasMoreThanOneDecimalPlace(diff.CircleSize)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); + + if (isOutOfRange(diff.CircleSize)) + yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); + + + if (hasMoreThanOneDecimalPlace(diff.DrainRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + + if (isOutOfRange(diff.DrainRate)) + yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + } + + private bool isOutOfRange(float setting) + { + return setting < 0f || setting > 10f; + } + + private bool hasMoreThanOneDecimalPlace(float setting) + { + return float.Round(setting, 1) != setting; + } + + public class IssueTemplateMoreThanOneDecimal : IssueTemplate + { + public IssueTemplateMoreThanOneDecimal(ICheck check) + : base(check, IssueType.Problem, "{0} {1} has more than one decimal place.") + { + } + + public Issue Create(string settingName, float settingValue) => new Issue(this, settingName, settingValue); + } + + public class IssueTemplateOutOfRange : IssueTemplate + { + public IssueTemplateOutOfRange(ICheck check) + : base(check, IssueType.Warning, "{0} is {1} although it is capped between 0 to 10 in-game.") + { + } + + public Issue Create(string settingName, float settingValue) => new Issue(this, settingName, settingValue); + } + } +} From c605e463a41388f43a0493d1246a813f1f7d4ab1 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 20 Mar 2024 15:52:16 -0300 Subject: [PATCH 087/581] Add mania keycount check --- .../Editor/Checks/CheckKeyCountTest.cs | 75 +++++++++++++++++++ .../Edit/Checks/CheckKeyCount.cs | 55 ++++++++++++++ .../Edit/ManiaBeatmapVerifier.cs | 24 ++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 + .../Checks/CheckAbnormalDifficultySettings.cs | 4 +- 5 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs new file mode 100644 index 0000000000..91361e2a62 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs @@ -0,0 +1,75 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Rulesets.Mania.Edit.Checks; +using System.Linq; + +namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks +{ + [TestFixture] + public class CheckKeyCountTest + { + private CheckKeyCount check = null!; + + private IBeatmap beatmap = null!; + + [SetUp] + public void Setup() + { + check = new CheckKeyCount(); + + beatmap = new Beatmap() + { + BeatmapInfo = new BeatmapInfo + { + Ruleset = new ManiaRuleset().RulesetInfo + } + }; + } + + [Test] + public void TestKeycountFour() + { + beatmap.Difficulty.CircleSize = 4; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestKeycountSmallerThanFour() + { + beatmap.Difficulty.CircleSize = 1; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckKeyCount.IssueTemplateKeycountTooLow); + } + + [Test] + public void TestKeycountHigherThanTen() + { + beatmap.Difficulty.CircleSize = 11; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckKeyCount.IssueTemplateKeycountNonStandard); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs b/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs new file mode 100644 index 0000000000..57991bd3ff --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs @@ -0,0 +1,55 @@ +// 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 osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Mania.Edit.Checks +{ + public class CheckKeyCount : ICheck + { + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Check mania keycount."); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateKeycountTooLow(this), + new IssueTemplateKeycountNonStandard(this), + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (diff.CircleSize < 4) + { + yield return new IssueTemplateKeycountTooLow(this).Create(diff.CircleSize); + } + + if (diff.CircleSize > 10) + { + yield return new IssueTemplateKeycountNonStandard(this).Create(diff.CircleSize); + } + } + + public class IssueTemplateKeycountTooLow : IssueTemplate + { + public IssueTemplateKeycountTooLow(ICheck check) + : base(check, IssueType.Problem, "Key count is {0} and must be 4 or higher.") + { + } + + public Issue Create(float current) => new Issue(this, current); + } + + public class IssueTemplateKeycountNonStandard : IssueTemplate + { + public IssueTemplateKeycountNonStandard(ICheck check) + : base(check, IssueType.Warning, "Key count {0} is higher than 10.") + { + } + + public Issue Create(float current) => new Issue(this, current); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs new file mode 100644 index 0000000000..778dcfc6e1 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs @@ -0,0 +1,24 @@ +// 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 System.Linq; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Mania.Edit.Checks; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class ManiaBeatmapVerifier : IBeatmapVerifier + { + private readonly List checks = new List + { + new CheckKeyCount(), + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + return checks.SelectMany(check => check.Run(context)); + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0b54fb3da0..3d4803f1e4 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -65,6 +65,8 @@ namespace osu.Game.Rulesets.Mania public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); + public override IBeatmapVerifier CreateBeatmapVerifier() => new ManiaBeatmapVerifier(); + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) { switch (skin) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs index 73049b323d..3c454a57fe 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Edit.Checks.Components; - namespace osu.Game.Rulesets.Edit.Checks { public class CheckAbnormalDifficultySettings : ICheck @@ -20,6 +19,7 @@ namespace osu.Game.Rulesets.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; + string ruleset = context.Beatmap.BeatmapInfo.Ruleset.ShortName; if (hasMoreThanOneDecimalPlace(diff.ApproachRate)) yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (hasMoreThanOneDecimalPlace(diff.CircleSize)) yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); - if (isOutOfRange(diff.CircleSize)) + if (ruleset != "mania" && isOutOfRange(diff.CircleSize)) yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); From 2d6a3b8e2b5e3f0023957868a0706f5df162e110 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 20 Mar 2024 16:51:27 -0300 Subject: [PATCH 088/581] Remove warning for 10K+ --- .../Editor/Checks/CheckKeyCountTest.cs | 12 ------------ .../Edit/Checks/CheckKeyCount.cs | 16 ---------------- 2 files changed, 28 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs index 91361e2a62..564c611548 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs @@ -55,18 +55,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks Assert.That(issues.Single().Template is CheckKeyCount.IssueTemplateKeycountTooLow); } - [Test] - public void TestKeycountHigherThanTen() - { - beatmap.Difficulty.CircleSize = 11; - - var context = getContext(); - var issues = check.Run(context).ToList(); - - Assert.That(issues, Has.Count.EqualTo(1)); - Assert.That(issues.Single().Template is CheckKeyCount.IssueTemplateKeycountNonStandard); - } - private BeatmapVerifierContext getContext() { return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); diff --git a/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs b/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs index 57991bd3ff..51ead5f423 100644 --- a/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs +++ b/osu.Game.Rulesets.Mania/Edit/Checks/CheckKeyCount.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Checks public IEnumerable PossibleTemplates => new IssueTemplate[] { new IssueTemplateKeycountTooLow(this), - new IssueTemplateKeycountNonStandard(this), }; public IEnumerable Run(BeatmapVerifierContext context) @@ -25,11 +24,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Checks { yield return new IssueTemplateKeycountTooLow(this).Create(diff.CircleSize); } - - if (diff.CircleSize > 10) - { - yield return new IssueTemplateKeycountNonStandard(this).Create(diff.CircleSize); - } } public class IssueTemplateKeycountTooLow : IssueTemplate @@ -41,15 +35,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Checks public Issue Create(float current) => new Issue(this, current); } - - public class IssueTemplateKeycountNonStandard : IssueTemplate - { - public IssueTemplateKeycountNonStandard(ICheck check) - : base(check, IssueType.Warning, "Key count {0} is higher than 10.") - { - } - - public Issue Create(float current) => new Issue(this, current); - } } } From b132734f9c429da05b976544b266528c765019e6 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 20 Mar 2024 18:54:38 -0300 Subject: [PATCH 089/581] Fix codefactor issues --- .../Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs index 3c454a57fe..db154f4efc 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs @@ -27,21 +27,18 @@ namespace osu.Game.Rulesets.Edit.Checks if (isOutOfRange(diff.ApproachRate)) yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); - if (hasMoreThanOneDecimalPlace(diff.OverallDifficulty)) yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); if (isOutOfRange(diff.OverallDifficulty)) yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); - if (hasMoreThanOneDecimalPlace(diff.CircleSize)) yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); if (ruleset != "mania" && isOutOfRange(diff.CircleSize)) yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); - if (hasMoreThanOneDecimalPlace(diff.DrainRate)) yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); From a4822fd615ef983be0399182f98159fc8546e427 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Fri, 22 Mar 2024 01:37:06 -0300 Subject: [PATCH 090/581] Make `CheckAbnormalDifficultySettings` abstract --- .../CheckAbnormalDifficultySettingsTest.cs | 93 ------------------- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 3 - .../Checks/CheckAbnormalDifficultySettings.cs | 41 ++------ 3 files changed, 8 insertions(+), 129 deletions(-) delete mode 100644 osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs b/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs deleted file mode 100644 index 94305755c4..0000000000 --- a/osu.Game.Tests/Editing/Checks/CheckAbnormalDifficultySettingsTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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.Edit.Checks; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Edit; -using osu.Game.Tests.Beatmaps; -using System.Linq; - -namespace osu.Game.Tests.Editing.Checks -{ - [TestFixture] - public class CheckAbnormalDifficultySettingsTest - { - private CheckAbnormalDifficultySettings check = null!; - - private IBeatmap beatmap = new Beatmap(); - - [SetUp] - public void Setup() - { - check = new CheckAbnormalDifficultySettings(); - beatmap.Difficulty = new(); - } - - [Test] - public void TestSettingsNormal() - { - var context = getContext(); - var issues = check.Run(context).ToList(); - - Assert.That(issues, Has.Count.EqualTo(0)); - } - - [Test] - public void TestAllSettingsMoreThanOneDecimal() - { - beatmap.Difficulty = new() - { - ApproachRate = 5.55f, - OverallDifficulty = 7.7777f, - CircleSize = 4.444f, - DrainRate = 1.1111111111f, - }; - - var context = getContext(); - var issues = check.Run(context).ToList(); - - Assert.That(issues, Has.Count.EqualTo(4)); - } - - [Test] - public void TestAllSettingsLessThanZero() - { - beatmap.Difficulty = new() - { - ApproachRate = -1, - OverallDifficulty = -20, - CircleSize = -11, - DrainRate = -34, - }; - - var context = getContext(); - var issues = check.Run(context).ToList(); - - Assert.That(issues, Has.Count.EqualTo(4)); - } - - [Test] - public void TestAllSettingsHigherThanTen() - { - beatmap.Difficulty = new() - { - ApproachRate = 14, - OverallDifficulty = 24, - CircleSize = 30, - DrainRate = 90, - }; - - var context = getContext(); - var issues = check.Run(context).ToList(); - - Assert.That(issues, Has.Count.EqualTo(4)); - } - - private BeatmapVerifierContext getContext() - { - return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); - } - } -} diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index edabe941b7..2eb52e88c7 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -42,9 +42,6 @@ namespace osu.Game.Rulesets.Edit // Events new CheckBreaks(), - - // Settings - new CheckAbnormalDifficultySettings(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs index db154f4efc..93592a866b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs @@ -6,9 +6,9 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks { - public class CheckAbnormalDifficultySettings : ICheck + public abstract class CheckAbnormalDifficultySettings : ICheck { - public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Abnormal difficulty settings"); + public abstract CheckMetadata Metadata { get; } public IEnumerable PossibleTemplates => new IssueTemplate[] { @@ -16,42 +16,17 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateOutOfRange(this), }; - public IEnumerable Run(BeatmapVerifierContext context) - { - var diff = context.Beatmap.Difficulty; - string ruleset = context.Beatmap.BeatmapInfo.Ruleset.ShortName; + public abstract IEnumerable Run(BeatmapVerifierContext context); - if (hasMoreThanOneDecimalPlace(diff.ApproachRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); - - if (isOutOfRange(diff.ApproachRate)) - yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); - - if (hasMoreThanOneDecimalPlace(diff.OverallDifficulty)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); - - if (isOutOfRange(diff.OverallDifficulty)) - yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); - - if (hasMoreThanOneDecimalPlace(diff.CircleSize)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); - - if (ruleset != "mania" && isOutOfRange(diff.CircleSize)) - yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); - - if (hasMoreThanOneDecimalPlace(diff.DrainRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); - - if (isOutOfRange(diff.DrainRate)) - yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); - } - - private bool isOutOfRange(float setting) + /// + /// If the setting is out of the boundaries set by the editor (0 - 10) + /// + protected bool OutOfRange(float setting) { return setting < 0f || setting > 10f; } - private bool hasMoreThanOneDecimalPlace(float setting) + protected bool HasMoreThanOneDecimalPlace(float setting) { return float.Round(setting, 1) != setting; } From e2df0981843c3428fa11a232d341d97ac3e0505f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Mar 2024 08:25:03 +0100 Subject: [PATCH 091/581] Add failing test case for desired artist sort behaviour --- .../SongSelect/TestSceneBeatmapCarousel.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index de2ae3708f..c0102b238c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -666,6 +666,56 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert($"Check {zzz_lowercase} is second last", () => carousel.BeatmapSets.SkipLast(1).Last().Metadata.Artist == zzz_lowercase); } + [Test] + public void TestSortByArtistUsesTitleAsTiebreaker() + { + var sets = new List(); + + AddStep("Populuate beatmap sets", () => + { + sets.Clear(); + + for (int i = 0; i < 20; i++) + { + var set = TestResources.CreateTestBeatmapSetInfo(); + + if (i == 4) + { + set.Beatmaps.ForEach(b => + { + b.Metadata.Artist = "ZZZ"; + b.Metadata.Title = "AAA"; + }); + } + + if (i == 8) + { + set.Beatmaps.ForEach(b => + { + b.Metadata.Artist = "ZZZ"; + b.Metadata.Title = "ZZZ"; + }); + } + + sets.Add(set); + } + }); + + loadBeatmaps(sets); + + AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false)); + AddAssert("Check last item", () => + { + var lastItem = carousel.BeatmapSets.Last(); + return lastItem.Metadata.Artist == "ZZZ" && lastItem.Metadata.Title == "ZZZ"; + }); + AddAssert("Check second last item", () => + { + var secondLastItem = carousel.BeatmapSets.SkipLast(1).Last(); + return secondLastItem.Metadata.Artist == "ZZZ" && secondLastItem.Metadata.Title == "AAA"; + }); + } + /// /// Ensures stability is maintained on different sort modes for items with equal properties. /// From a1880e89c299f55a8907050988b8200e854ed34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Mar 2024 08:31:59 +0100 Subject: [PATCH 092/581] Use title as tiebreaker when sorting beatmap carousel by artist Closes https://github.com/ppy/osu/issues/27548. Reference: https://github.com/peppy/osu-stable-reference/blob/e53980dd76857ee899f66ce519ba1597e7874f28/osu!/GameplayElements/Beatmaps/BeatmapTreeManager.cs#L341-L347 --- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 43c9c621e8..7e15699804 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Select.Carousel default: case SortMode.Artist: comparison = OrdinalSortByCaseStringComparer.DEFAULT.Compare(BeatmapSet.Metadata.Artist, otherSet.BeatmapSet.Metadata.Artist); + if (comparison == 0) + goto case SortMode.Title; break; case SortMode.Title: From 6fa663c8cac732c6d705955f52565f1f78b7eddd Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Fri, 22 Mar 2024 14:48:22 -0300 Subject: [PATCH 093/581] Make check ruleset specific --- ...heckCatchAbnormalDifficultySettingsTest.cs | 158 ++++++++++++++ .../Edit/CatchBeatmapVerifier.cs | 3 +- .../CheckCatchAbnormalDifficultySettings.cs | 38 ++++ ...heckManiaAbnormalDifficultySettingsTest.cs | 121 +++++++++++ .../CheckManiaAbnormalDifficultySettings.cs | 32 +++ .../Edit/ManiaBeatmapVerifier.cs | 2 + .../CheckOsuAbnormalDifficultySettingsTest.cs | 194 ++++++++++++++++++ .../CheckOsuAbnormalDifficultySettings.cs | 43 ++++ .../Edit/OsuBeatmapVerifier.cs | 3 + ...heckTaikoAbnormalDifficultySettingsTest.cs | 84 ++++++++ .../CheckTaikoAbnormalDifficultySettings.cs | 26 +++ .../Edit/TaikoBeatmapVerifier.cs | 24 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 + 13 files changed, 729 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs create mode 100644 osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/TaikoBeatmapVerifier.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs new file mode 100644 index 0000000000..2ae2e20215 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs @@ -0,0 +1,158 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit.Checks; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests.Editor.Checks +{ + [TestFixture] + public class CheckCatchAbnormalDifficultySettingsTest + { + private CheckCatchAbnormalDifficultySettings check = null!; + + private IBeatmap beatmap = new Beatmap(); + + [SetUp] + public void Setup() + { + check = new CheckCatchAbnormalDifficultySettings(); + + beatmap.BeatmapInfo.Ruleset = new CatchRuleset().RulesetInfo; + beatmap.Difficulty = new BeatmapDifficulty() + { + ApproachRate = 5, + CircleSize = 5, + DrainRate = 5, + }; + } + + [Test] + public void TestNormalSettings() + { + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestApproachRateTwoDecimals() + { + beatmap.Difficulty.ApproachRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestCircleSizeTwoDecimals() + { + beatmap.Difficulty.CircleSize = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestDrainRateTwoDecimals() + { + beatmap.Difficulty.DrainRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestApproachRateUnder() + { + beatmap.Difficulty.ApproachRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestCircleSizeUnder() + { + beatmap.Difficulty.CircleSize = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateUnder() + { + beatmap.Difficulty.DrainRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestApproachRateOver() + { + beatmap.Difficulty.ApproachRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestCircleSizeOver() + { + beatmap.Difficulty.CircleSize = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateOver() + { + beatmap.Difficulty.DrainRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBeatmapVerifier.cs b/osu.Game.Rulesets.Catch/Edit/CatchBeatmapVerifier.cs index c7a41a4e22..71da6d5014 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchBeatmapVerifier.cs @@ -13,7 +13,8 @@ namespace osu.Game.Rulesets.Catch.Edit { private readonly List checks = new List { - new CheckBananaShowerGap() + new CheckBananaShowerGap(), + new CheckCatchAbnormalDifficultySettings(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs new file mode 100644 index 0000000000..8295795f00 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs @@ -0,0 +1,38 @@ +// 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 osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Catch.Edit.Checks +{ + public class CheckCatchAbnormalDifficultySettings : CheckAbnormalDifficultySettings + { + public override CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Checks catch relevant settings"); + + public override IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (HasMoreThanOneDecimalPlace(diff.ApproachRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); + + if (OutOfRange(diff.ApproachRate)) + yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); + + if (HasMoreThanOneDecimalPlace(diff.CircleSize)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); + + if (OutOfRange(diff.CircleSize)) + yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); + + if (HasMoreThanOneDecimalPlace(diff.DrainRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + + if (OutOfRange(diff.DrainRate)) + yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs new file mode 100644 index 0000000000..6c585aace3 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs @@ -0,0 +1,121 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Edit.Checks; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks +{ + [TestFixture] + public class CheckManiaAbnormalDifficultySettingsTest + { + private CheckManiaAbnormalDifficultySettings check = null!; + + private IBeatmap beatmap = new Beatmap(); + + [SetUp] + public void Setup() + { + check = new CheckManiaAbnormalDifficultySettings(); + + beatmap.BeatmapInfo.Ruleset = new ManiaRuleset().RulesetInfo; + beatmap.Difficulty = new BeatmapDifficulty() + { + OverallDifficulty = 5, + DrainRate = 5, + }; + } + + [Test] + public void TestNormalSettings() + { + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestOverallDifficultyTwoDecimals() + { + beatmap.Difficulty.OverallDifficulty = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestDrainRateTwoDecimals() + { + beatmap.Difficulty.DrainRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestOverallDifficultyUnder() + { + beatmap.Difficulty.OverallDifficulty = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateUnder() + { + beatmap.Difficulty.DrainRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestOverallDifficultyOver() + { + beatmap.Difficulty.OverallDifficulty = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateOver() + { + beatmap.Difficulty.DrainRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs new file mode 100644 index 0000000000..ae0cc3aa4c --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs @@ -0,0 +1,32 @@ +// 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 osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Mania.Edit.Checks +{ + public class CheckManiaAbnormalDifficultySettings : CheckAbnormalDifficultySettings + { + public override CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Checks mania relevant settings"); + + public override IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (OutOfRange(diff.OverallDifficulty)) + yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (HasMoreThanOneDecimalPlace(diff.DrainRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + + if (OutOfRange(diff.DrainRate)) + yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs index 778dcfc6e1..4adabfa4d7 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatmapVerifier.cs @@ -13,7 +13,9 @@ namespace osu.Game.Rulesets.Mania.Edit { private readonly List checks = new List { + // Settings new CheckKeyCount(), + new CheckManiaAbnormalDifficultySettings(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs new file mode 100644 index 0000000000..53ccd3c7a7 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs @@ -0,0 +1,194 @@ +// 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.Osu.Edit.Checks; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit; +using osu.Game.Tests.Beatmaps; +using System.Linq; + +namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks +{ + [TestFixture] + public class CheckOsuAbnormalDifficultySettingsTest + { + private CheckOsuAbnormalDifficultySettings check = null!; + + private IBeatmap beatmap = new Beatmap(); + + [SetUp] + public void Setup() + { + check = new CheckOsuAbnormalDifficultySettings(); + + beatmap.Difficulty = new BeatmapDifficulty() + { + ApproachRate = 5, + CircleSize = 5, + DrainRate = 5, + OverallDifficulty = 5, + }; + } + + [Test] + public void TestNormalSettings() + { + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestApproachRateTwoDecimals() + { + beatmap.Difficulty.ApproachRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestCircleSizeTwoDecimals() + { + beatmap.Difficulty.CircleSize = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestDrainRateTwoDecimals() + { + beatmap.Difficulty.DrainRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestOverallDifficultyTwoDecimals() + { + beatmap.Difficulty.OverallDifficulty = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestApproachRateUnder() + { + beatmap.Difficulty.ApproachRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestCircleSizeUnder() + { + beatmap.Difficulty.CircleSize = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateUnder() + { + beatmap.Difficulty.DrainRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestOverallDifficultyUnder() + { + beatmap.Difficulty.OverallDifficulty = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestApproachRateOver() + { + beatmap.Difficulty.ApproachRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestCircleSizeOver() + { + beatmap.Difficulty.CircleSize = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateOver() + { + beatmap.Difficulty.DrainRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestOverallDifficultyOver() + { + beatmap.Difficulty.OverallDifficulty = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs new file mode 100644 index 0000000000..c1eca7fff7 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs @@ -0,0 +1,43 @@ +// 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 osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckOsuAbnormalDifficultySettings : CheckAbnormalDifficultySettings + { + public override CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Checks osu relevant settings"); + public override IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (HasMoreThanOneDecimalPlace(diff.ApproachRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); + + if (OutOfRange(diff.ApproachRate)) + yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); + + if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (OutOfRange(diff.OverallDifficulty)) + yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (HasMoreThanOneDecimalPlace(diff.CircleSize)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); + + if (OutOfRange(diff.CircleSize)) + yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); + + if (HasMoreThanOneDecimalPlace(diff.DrainRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + + if (OutOfRange(diff.DrainRate)) + yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 325e9ed4cb..4b01a1fc39 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -21,6 +21,9 @@ namespace osu.Game.Rulesets.Osu.Edit new CheckTimeDistanceEquality(), new CheckLowDiffOverlaps(), new CheckTooShortSliders(), + + // Settings + new CheckOsuAbnormalDifficultySettings(), }; public IEnumerable Run(BeatmapVerifierContext context) diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs new file mode 100644 index 0000000000..f10e62f3bf --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs @@ -0,0 +1,84 @@ +// 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.Taiko.Edit.Checks; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit; +using osu.Game.Tests.Beatmaps; +using System.Linq; + +namespace osu.Game.Rulesets.Taiko.Tests.Editor.Checks +{ + [TestFixture] + public class CheckTaikoAbnormalDifficultySettingsTest + { + private CheckTaikoAbnormalDifficultySettings check = null!; + + private IBeatmap beatmap = new Beatmap(); + + [SetUp] + public void Setup() + { + check = new CheckTaikoAbnormalDifficultySettings(); + + beatmap.BeatmapInfo.Ruleset = new TaikoRuleset().RulesetInfo; + beatmap.Difficulty = new BeatmapDifficulty() + { + OverallDifficulty = 5, + }; + } + + [Test] + public void TestNormalSettings() + { + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestOverallDifficultyTwoDecimals() + { + beatmap.Difficulty.OverallDifficulty = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + + [Test] + public void TestOverallDifficultyUnder() + { + beatmap.Difficulty.OverallDifficulty = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestOverallDifficultyOver() + { + beatmap.Difficulty.OverallDifficulty = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + private BeatmapVerifierContext getContext() + { + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs new file mode 100644 index 0000000000..ce35f21853 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs @@ -0,0 +1,26 @@ +// 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 osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Taiko.Edit.Checks +{ + public class CheckTaikoAbnormalDifficultySettings : CheckAbnormalDifficultySettings + { + public override CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Checks taiko relevant settings"); + + public override IEnumerable Run(BeatmapVerifierContext context) + { + var diff = context.Beatmap.Difficulty; + + if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (OutOfRange(diff.OverallDifficulty)) + yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBeatmapVerifier.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBeatmapVerifier.cs new file mode 100644 index 0000000000..f5c3f1846d --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBeatmapVerifier.cs @@ -0,0 +1,24 @@ +// 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 System.Linq; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Taiko.Edit.Checks; + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class TaikoBeatmapVerifier : IBeatmapVerifier + { + private readonly List checks = new List + { + new CheckTaikoAbnormalDifficultySettings(), + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + return checks.SelectMany(check => check.Run(context)); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 24b0ec5d57..d7184bce60 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -188,6 +188,8 @@ namespace osu.Game.Rulesets.Taiko public override HitObjectComposer CreateHitObjectComposer() => new TaikoHitObjectComposer(this); + public override IBeatmapVerifier CreateBeatmapVerifier() => new TaikoBeatmapVerifier(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(RulesetInfo, beatmap); public override PerformanceCalculator CreatePerformanceCalculator() => new TaikoPerformanceCalculator(); From c7c03302653790668d0fdb49f25dfceb016eaf3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Mar 2024 19:05:58 +0100 Subject: [PATCH 094/581] Attempt to disable rulesets that can be linked to an unhandled crash --- osu.Game/OsuGameBase.cs | 4 ++- osu.Game/Rulesets/RealmRulesetStore.cs | 48 ++++++++++++++++++++++++-- osu.Game/Rulesets/RulesetStore.cs | 24 +++++++++---- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 81e3d8bed8..8bda8fb6c2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -678,12 +678,14 @@ namespace osu.Game /// /// Allows a maximum of one unhandled exception, per second of execution. /// - private bool onExceptionThrown(Exception _) + private bool onExceptionThrown(Exception ex) { bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); + RulesetStore.TryDisableCustomRulesetsCausing(ex); + // restore the stock of allowable exceptions after a short delay. Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); diff --git a/osu.Game/Rulesets/RealmRulesetStore.cs b/osu.Game/Rulesets/RealmRulesetStore.cs index ba6f4583d1..36eae7af2c 100644 --- a/osu.Game/Rulesets/RealmRulesetStore.cs +++ b/osu.Game/Rulesets/RealmRulesetStore.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; @@ -13,17 +16,20 @@ namespace osu.Game.Rulesets { public class RealmRulesetStore : RulesetStore { + private readonly RealmAccess realmAccess; public override IEnumerable AvailableRulesets => availableRulesets; private readonly List availableRulesets = new List(); - public RealmRulesetStore(RealmAccess realm, Storage? storage = null) + public RealmRulesetStore(RealmAccess realmAccess, Storage? storage = null) : base(storage) { - prepareDetachedRulesets(realm); + this.realmAccess = realmAccess; + prepareDetachedRulesets(); + informUserAboutBrokenRulesets(); } - private void prepareDetachedRulesets(RealmAccess realmAccess) + private void prepareDetachedRulesets() { realmAccess.Write(realm => { @@ -143,5 +149,41 @@ namespace osu.Game.Rulesets instance.CreateBeatmapProcessor(converter.Convert()); } + + private void informUserAboutBrokenRulesets() + { + if (RulesetStorage == null) + return; + + foreach (string brokenRulesetDll in RulesetStorage.GetFiles(@".", @"*.dll.broken")) + { + Logger.Log($"Ruleset '{Path.GetFileNameWithoutExtension(brokenRulesetDll)}' has been disabled due to causing a crash.\n\n" + + "Please update the ruleset or report the issue to the developers of the ruleset if no updates are available.", level: LogLevel.Important); + } + } + + internal void TryDisableCustomRulesetsCausing(Exception exception) + { + var stackTrace = new StackTrace(exception); + + foreach (var frame in stackTrace.GetFrames()) + { + var declaringAssembly = frame.GetMethod()?.DeclaringType?.Assembly; + if (declaringAssembly == null) + continue; + + if (UserRulesetAssemblies.Contains(declaringAssembly)) + { + string sourceLocation = declaringAssembly.Location; + string destinationLocation = Path.ChangeExtension(sourceLocation, @".dll.broken"); + + if (File.Exists(sourceLocation)) + { + Logger.Log($"Unhandled exception traced back to custom ruleset {Path.GetFileNameWithoutExtension(sourceLocation)}. Marking as broken."); + File.Move(sourceLocation, destinationLocation); + } + } + } + } } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index ac36ee6494..f33d42a53e 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets private const string ruleset_library_prefix = @"osu.Game.Rulesets"; protected readonly Dictionary LoadedAssemblies = new Dictionary(); + protected readonly HashSet UserRulesetAssemblies = new HashSet(); + protected readonly Storage? RulesetStorage; /// /// All available rulesets. @@ -41,9 +43,9 @@ namespace osu.Game.Rulesets // to load as unable to locate the game core assembly. AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; - var rulesetStorage = storage?.GetStorageForDirectory(@"rulesets"); - if (rulesetStorage != null) - loadUserRulesets(rulesetStorage); + RulesetStorage = storage?.GetStorageForDirectory(@"rulesets"); + if (RulesetStorage != null) + loadUserRulesets(RulesetStorage); } /// @@ -105,7 +107,11 @@ namespace osu.Game.Rulesets var rulesets = rulesetStorage.GetFiles(@".", @$"{ruleset_library_prefix}.*.dll"); foreach (string? ruleset in rulesets.Where(f => !f.Contains(@"Tests"))) - loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); + { + var assembly = loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); + if (assembly != null) + UserRulesetAssemblies.Add(assembly); + } } private void loadFromDisk() @@ -126,21 +132,25 @@ namespace osu.Game.Rulesets } } - private void loadRulesetFromFile(string file) + private Assembly? loadRulesetFromFile(string file) { string filename = Path.GetFileNameWithoutExtension(file); if (LoadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename)) - return; + return null; try { - addRuleset(Assembly.LoadFrom(file)); + var assembly = Assembly.LoadFrom(file); + addRuleset(assembly); + return assembly; } catch (Exception e) { LogFailedLoad(filename, e); } + + return null; } private void addRuleset(Assembly assembly) From 6fbe1a5b8d1a80c6a5b1e277ee015162b7ea8083 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Sat, 23 Mar 2024 19:22:47 -0300 Subject: [PATCH 095/581] Add video resolution check --- .../Checks/CheckVideoResolutionTest.cs | 86 +++++++++++++ .../Videos/test-video-resolution-high.mp4 | Bin 0 -> 13655 bytes osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 1 + .../Edit/Checks/CheckVideoResolution.cs | 117 ++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs create mode 100644 osu.Game.Tests/Resources/Videos/test-video-resolution-high.mp4 create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs b/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs new file mode 100644 index 0000000000..ab677a15d4 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs @@ -0,0 +1,86 @@ +using System.IO; +using System.Linq; +using Moq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Storyboards; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckVideoResolutionTest + { + private CheckVideoResolution check = null!; + + private IBeatmap beatmap = null!; + + [SetUp] + public void Setup() + { + check = new CheckVideoResolution(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo + { + Files = + { + CheckTestHelpers.CreateMockFile("mp4"), + } + } + } + }; + } + + [Test] + public void TestNoVideo() + { + beatmap.BeatmapInfo.BeatmapSet?.Files.Clear(); + + var issues = check.Run(getContext(null)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(0)); + } + + [Test] + public void TestVideoAcceptableResolution() + { + using (var resourceStream = TestResources.OpenResource("Videos/test-video.mp4")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + + [Test] + public void TestVideoHighResolution() + { + using (var resourceStream = TestResources.OpenResource("Videos/test-video-resolution-high.mp4")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckVideoResolution.IssueTemplateHighResolution); + } + } + + private BeatmapVerifierContext getContext(Stream? resourceStream) + { + var storyboard = new Storyboard(); + var layer = storyboard.GetLayer("Video"); + layer.Add(new StoryboardVideo("abc123.mp4", 0)); + + var mockWorkingBeatmap = new Mock(beatmap, null, null); + mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny())).Returns(resourceStream); + mockWorkingBeatmap.As().SetupGet(w => w.Storyboard).Returns(storyboard); + + return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object); + } + } +} diff --git a/osu.Game.Tests/Resources/Videos/test-video-resolution-high.mp4 b/osu.Game.Tests/Resources/Videos/test-video-resolution-high.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..fbdb00d3ad464a7f2d9d223d5fa9edb67fdaa689 GIT binary patch literal 13655 zcmeHNeQX>@6@PczVL*m4tPP}$L-S}hg&bg*ePO=GZEeMbb5vo#p-P>LJ4)5Ns zch`~B8a%$qmwy`2jYNdw-vopmjfC`>E|#gtJsYT8{RYT1XfIi(P|$oB0*sq zpGtgdCop3W(sEt%T;z?q;d(ye&y`r0=D_UPkB{GRO@&2S21 z&}dfEgEGsv;N9w1a+;yDHdS9Tv$+8j7L1zyp!~F#Yx%lH&CyvKmRS9q5yz17<*GYe zFKwWF+V$LELs*+*Y}1gl#EwF5+qsKNF8ok7uW)6Fy!Ny38Jjq{as2fL>sSr>ss76H3uE$U3*>WF{yW;; zS7^sF$iI3!awdDWcf;49u{YO+Uj+8=_C5X|^ksaRZv88BzT$eSg+46Z3hUPkE&d4T z1?bT2pq~)cuLpgPXkjPl+eC|&f%Zdfmx3N8TKrznZWtB?^fJ+sEudc#E!_+{3U&Pe z=xM0)I?#8Cn&Y4+iCR{H9wSK@P;qSbeTP7$?k0{x8W<{l8{ zvt~W$5K$ZE({_NU^M24Ik&Ly=uMkBupmRjAH0U%@9P^2vAnLjU^gL0`iECI}{{Z+_ zo=GI1LQF|~ttkM0=7S}mr&MI6x3achDOuZ^zPGlvPft+Du4rvtYb3|B`WW7|lC`bA zT(GuvZr<7kZT|^NTm6HArM-WerOm-LVsrgeT6UP~7=o#?l+ay`DLq9dCMH(koK+!T z)Kkx(FgroBZ>$DVJ@26{=copy5EFID92VLDn{E#8&Rf55hC9wNv|}>~h>gXFy))Yt z;ME*n!{N0Y&TK}|el~~Sz~OT^d@hH-hr@5=a3+~xKJz#nN2Q1}+Zym?VhY+XmBZI?cpHbe zb2!{uaU2});yh&zk8(KEgFtRw9KM#r6C8dEhr=8d$A{@7rYn$3=yTKD0rZd9J}~{6 zAu%$T=9cDT-{!;gFM#P^0MlO&-KdA@UkF#Ua38h@`?Kg#m=2tghTU*SIIoN0!W%yc z`W4*KX3$Z%BeAh$M+~HoECS8%R*Y8ZoV8d3r?i6y$fAx}T#+ z&3t$cC*vr#%|^2*}0oF;OLOU>ZK-H zVOmerR8LKP=XZU7I(+_g|BoJS`;ByZ*`>dX#S&doyQEr56N-{R zMSlkxbbfeq-|g*PQty_HXsPKcDmGg7xS(;Fu?DN7h zlw61940bXu9U0Fw(8jX8td(=SB~?i)YBDO}6CxQ+NSdBDEOjUu?UuV`Ny#b3xT`1S zv3R#U7LCUxQ%{bV8Oa?`lrBn7e%2^GINT(D{u|2<(&MGcr zl67>fD=ux-vx6Bg4Q7_D=Td`~joLhDqr;FsjcsjzFMW01iHl#4H$78-=&7z- z4xjYDkRrje_QmgaED5ki<63Bk!l=n^X-e2CzC5w+{s z<0>4<%Q!UP3&&p^b*3;Qfwx5h!@V%cR_`OT+jo9+>Wx=Ks94RF--nr3P~jEgQFw(I zyauYL9p1w7Qi3SIZ& z%Mf(K7%uP7rKTTw9q2M{o*u$IJTOVZS6C0xX!(fObUFh3+6;^T0SSbM0*w|PoaGr8 z*^7+GZM?OX0CtN#FS<<~4c6f5rYbqvVZ z(nW@16+U&ug<=(SORYVicoAHevAaoT#n7$^<57MaF=7Y|I1}+YI4_; zWvcsMx&r+1wrR28bqL9r)&9TvrLxR$iVManI16#X*fgu{@3ydztX$xtgRx>%1PvD# nl9krXg0TnQ0l!rC9a$sfC&QIzd5O0gqJFh2Q*qv;=F9&8=0zb8 literal 0 HcmV?d00001 diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 4a316afd22..95f79391ef 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Edit // Resources new CheckBackgroundPresence(), new CheckBackgroundQuality(), + new CheckVideoResolution(), // Audio new CheckAudioPresence(), diff --git a/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs b/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs new file mode 100644 index 0000000000..831962e2e9 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs @@ -0,0 +1,117 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.IO.FileAbstraction; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Storyboards; +using TagLib; +using File = TagLib.File; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckVideoResolution : ICheck + { + private const int max_video_width = 1280; + + private const int max_video_height = 720; + + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Resources, "Too high video resolution."); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateHighResolution(this), + new IssueTemplateFileError(this), + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet; + var videoPaths = getVideoPaths(context.WorkingBeatmap.Storyboard); + + foreach (string filename in videoPaths) + { + string? storagePath = beatmapSet?.GetPathForFile(filename); + + // Don't report any issues for missing video here since another check is already doing that (CheckAudioInVideo) + if (storagePath == null) continue; + + Issue issue; + + try + { + using (Stream data = context.WorkingBeatmap.GetStream(storagePath)) + using (File tagFile = File.Create(new StreamFileAbstraction(filename, data))) + { + int height = tagFile.Properties.VideoHeight; + int width = tagFile.Properties.VideoWidth; + + if (height <= max_video_height || width <= max_video_width) + continue; + + issue = new IssueTemplateHighResolution(this).Create(filename, width, height); + } + } + catch (CorruptFileException) + { + issue = new IssueTemplateFileError(this).Create(filename, "Corrupt file"); + } + catch (UnsupportedFormatException) + { + issue = new IssueTemplateFileError(this).Create(filename, "Unsupported format"); + } + catch (Exception ex) + { + issue = new IssueTemplateFileError(this).Create(filename, "Internal failure - see logs for more info"); + Logger.Log($"Failed when running {nameof(CheckVideoResolution)}: {ex}"); + } + + yield return issue; + } + } + + private List getVideoPaths(Storyboard storyboard) + { + var videoPaths = new List(); + + foreach (var layer in storyboard.Layers) + { + foreach (var element in layer.Elements) + { + if (element is not StoryboardVideo video) + continue; + + if (!videoPaths.Contains(video.Path)) + videoPaths.Add(video.Path); + } + } + + return videoPaths; + } + + public class IssueTemplateHighResolution : IssueTemplate + { + public IssueTemplateHighResolution(ICheck check) + : base(check, IssueType.Problem, "\"{0}\" resolution exceeds 1280x720 ({1}x{2})") + { + } + + public Issue Create(string filename, int width, int height) => new Issue(this, filename, width, height); + } + + public class IssueTemplateFileError : IssueTemplate + { + public IssueTemplateFileError(ICheck check) + : base(check, IssueType.Error, "Could not check resolution for \"{0}\" ({1}).") + { + } + + public Issue Create(string filename, string errorReason) => new Issue(this, filename, errorReason); + } + + } +} From eb938272040be14caa292fe65800356a6ae03a1a Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Sat, 23 Mar 2024 23:11:13 -0300 Subject: [PATCH 096/581] Add missing copyright header --- osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs b/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs index ab677a15d4..1e16c67aab 100644 --- a/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckVideoResolutionTest.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.IO; using System.Linq; using Moq; From 8a05fecad5862d744d49b8febcb389e4e22b9850 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Sat, 23 Mar 2024 23:28:55 -0300 Subject: [PATCH 097/581] Fix formatting issue --- osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs b/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs index 831962e2e9..1b603b7e47 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckVideoResolution.cs @@ -112,6 +112,5 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, string errorReason) => new Issue(this, filename, errorReason); } - } } From ef2a16dd8ff260efa5117a10352e6ec771dfa2c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Mar 2024 16:41:40 +0800 Subject: [PATCH 098/581] Various renaming and class updates to allow multiple menu banners --- .../Visual/Menus/TestSceneMainMenu.cs | 46 +++++++++----- ...tleRequest.cs => GetMenuContentRequest.cs} | 4 +- .../API/Requests/Responses/APIMenuContent.cs | 42 +++++++++++++ .../API/Requests/Responses/APIMenuImage.cs | 57 +++++++++++++++++ .../API/Requests/Responses/APISystemTitle.cs | 30 --------- osu.Game/Screens/Menu/MainMenu.cs | 8 +-- .../{SystemTitle.cs => OnlineMenuBanner.cs} | 62 ++++++++++--------- 7 files changed, 169 insertions(+), 80 deletions(-) rename osu.Game/Online/API/Requests/{GetSystemTitleRequest.cs => GetMenuContentRequest.cs} (74%) create mode 100644 osu.Game/Online/API/Requests/Responses/APIMenuContent.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIMenuImage.cs delete mode 100644 osu.Game/Online/API/Requests/Responses/APISystemTitle.cs rename osu.Game/Screens/Menu/{SystemTitle.cs => OnlineMenuBanner.cs} (79%) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 7053a9d544..3c78edb8a5 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -13,30 +13,48 @@ namespace osu.Game.Tests.Visual.Menus { public partial class TestSceneMainMenu : OsuGameTestScene { - private SystemTitle systemTitle => Game.ChildrenOfType().Single(); + private OnlineMenuBanner onlineMenuBanner => Game.ChildrenOfType().Single(); [Test] - public void TestSystemTitle() + public void TestOnlineMenuBanner() { - AddStep("set system title", () => systemTitle.Current.Value = new APISystemTitle + AddStep("set online content", () => onlineMenuBanner.Current.Value = new APIMenuContent { - Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", - Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + } + } }); - AddAssert("system title not visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Hidden)); + AddAssert("system title not visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Hidden)); AddStep("enter menu", () => InputManager.Key(Key.Enter)); - AddUntilStep("system title visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Visible)); - AddStep("set another title", () => systemTitle.Current.Value = new APISystemTitle + AddUntilStep("system title visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Visible)); + AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent { - Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", - Url = @"https://osu.ppy.sh/community/contests/189", + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", + Url = @"https://osu.ppy.sh/community/contests/189", + } + } }); - AddStep("set title with nonexistent image", () => systemTitle.Current.Value = new APISystemTitle + AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent { - Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 - Url = @"https://osu.ppy.sh/community/contests/189", + Images = new[] + { + new APIMenuImage + { + Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 + Url = @"https://osu.ppy.sh/community/contests/189", + } + } }); - AddStep("unset system title", () => systemTitle.Current.Value = null); + AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); } } } diff --git a/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs b/osu.Game/Online/API/Requests/GetMenuContentRequest.cs similarity index 74% rename from osu.Game/Online/API/Requests/GetSystemTitleRequest.cs rename to osu.Game/Online/API/Requests/GetMenuContentRequest.cs index 52ca0c11eb..ad2bac6696 100644 --- a/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs +++ b/osu.Game/Online/API/Requests/GetMenuContentRequest.cs @@ -5,9 +5,9 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests { - public class GetSystemTitleRequest : OsuJsonWebRequest + public class GetMenuContentRequest : OsuJsonWebRequest { - public GetSystemTitleRequest() + public GetMenuContentRequest() : base(@"https://assets.ppy.sh/lazer-status.json") { } diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs new file mode 100644 index 0000000000..acee6c99ba --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIMenuContent : IEquatable + { + /// + /// Images which should be displayed in rotation. + /// + [JsonProperty(@"images")] + public APIMenuImage[] Images { get; init; } = Array.Empty(); + + public DateTimeOffset LastUpdated { get; init; } + + public bool Equals(APIMenuContent? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return LastUpdated.Equals(other.LastUpdated); + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + + if (obj.GetType() != GetType()) return false; + + return Equals((APIMenuContent)obj); + } + + public override int GetHashCode() + { + return LastUpdated.GetHashCode(); + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs new file mode 100644 index 0000000000..4824e23d4b --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIMenuImage : IEquatable + { + /// + /// A URL pointing to the image which should be displayed. Generally should be an @2x image filename. + /// + [JsonProperty(@"image")] + public string Image { get; init; } = string.Empty; + + /// + /// A URL that should be opened on clicking the image. + /// + [JsonProperty(@"url")] + public string Url { get; init; } = string.Empty; + + /// + /// The time at which this item should begin displaying. If null, will display immediately. + /// + [JsonProperty(@"begins")] + public DateTimeOffset? Begins { get; set; } + + /// + /// The time at which this item should stop displaying. If null, will display indefinitely. + /// + [JsonProperty(@"expires")] + public DateTimeOffset? Expires { get; set; } + + public bool Equals(APIMenuImage? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Image == other.Image && Url == other.Url; + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + + return Equals((APIMenuImage)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Image, Url); + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs deleted file mode 100644 index bfa5c1043b..0000000000 --- a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using Newtonsoft.Json; - -namespace osu.Game.Online.API.Requests.Responses -{ - public class APISystemTitle : IEquatable - { - [JsonProperty(@"image")] - public string Image { get; set; } = string.Empty; - - [JsonProperty(@"url")] - public string Url { get; set; } = string.Empty; - - public bool Equals(APISystemTitle? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - - return Image == other.Image && Url == other.Url; - } - - public override bool Equals(object? obj) => obj is APISystemTitle other && Equals(other); - - // ReSharper disable NonReadonlyMemberInGetHashCode - public override int GetHashCode() => HashCode.Combine(Image, Url); - } -} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index decb901c32..235c5d5c56 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -98,7 +98,7 @@ namespace osu.Game.Screens.Menu private ParallaxContainer buttonsContainer; private SongTicker songTicker; private Container logoTarget; - private SystemTitle systemTitle; + private OnlineMenuBanner onlineMenuBanner; private MenuTip menuTip; private FillFlowContainer bottomElementsFlow; private SupporterDisplay supporterDisplay; @@ -178,7 +178,7 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - systemTitle = new SystemTitle + onlineMenuBanner = new OnlineMenuBanner { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -201,12 +201,12 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Initial: case ButtonSystemState.Exit: ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine)); - systemTitle.State.Value = Visibility.Hidden; + onlineMenuBanner.State.Value = Visibility.Hidden; break; default: ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine)); - systemTitle.State.Value = Visibility.Visible; + onlineMenuBanner.State.Value = Visibility.Visible; break; } }; diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs similarity index 79% rename from osu.Game/Screens/Menu/SystemTitle.cs rename to osu.Game/Screens/Menu/OnlineMenuBanner.cs index 813a470ed6..cf20196f85 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -18,42 +19,28 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Screens.Menu { - public partial class SystemTitle : VisibilityContainer + public partial class OnlineMenuBanner : VisibilityContainer { - internal Bindable Current { get; } = new Bindable(); + internal Bindable Current { get; } = new Bindable(new APIMenuContent()); private const float transition_duration = 500; private Container content = null!; private CancellationTokenSource? cancellationTokenSource; - private SystemTitleImage? currentImage; - - private ScheduledDelegate? openUrlAction; + private MenuImage? currentImage; [BackgroundDependencyLoader] - private void load(OsuGame? game) + private void load() { AutoSizeAxes = Axes.Both; AutoSizeDuration = transition_duration; AutoSizeEasing = Easing.OutQuint; - InternalChild = content = new OsuClickableContainer + InternalChild = content = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, - Action = () => - { - currentImage?.Flash(); - - // Delay slightly to allow animation to play out. - openUrlAction?.Cancel(); - openUrlAction = Scheduler.AddDelayed(() => - { - if (!string.IsNullOrEmpty(Current.Value?.Url)) - game?.HandleLink(Current.Value.Url); - }, 250); - } }; } @@ -98,7 +85,7 @@ namespace osu.Game.Screens.Menu private void checkForUpdates() { - var request = new GetSystemTitleRequest(); + var request = new GetMenuContentRequest(); Task.Run(() => request.Perform()) .ContinueWith(r => { @@ -121,12 +108,12 @@ namespace osu.Game.Screens.Menu cancellationTokenSource = null; currentImage?.FadeOut(500, Easing.OutQuint).Expire(); - if (string.IsNullOrEmpty(Current.Value?.Image)) + if (Current.Value.Images.Length == 0) return; - LoadComponentAsync(new SystemTitleImage(Current.Value), loaded => + LoadComponentAsync(new MenuImage(Current.Value.Images.First()), loaded => { - if (!loaded.SystemTitle.Equals(Current.Value)) + if (!loaded.Image.Equals(Current.Value.Images.First())) loaded.Dispose(); content.Add(currentImage = loaded); @@ -134,22 +121,24 @@ namespace osu.Game.Screens.Menu } [LongRunningLoad] - private partial class SystemTitleImage : CompositeDrawable + private partial class MenuImage : OsuClickableContainer { - public readonly APISystemTitle SystemTitle; + public readonly APIMenuImage Image; private Sprite flash = null!; - public SystemTitleImage(APISystemTitle systemTitle) + private ScheduledDelegate? openUrlAction; + + public MenuImage(APIMenuImage image) { - SystemTitle = systemTitle; + Image = image; } [BackgroundDependencyLoader] - private void load(LargeTextureStore textureStore) + private void load(LargeTextureStore textureStore, OsuGame game) { - Texture? texture = textureStore.Get(SystemTitle.Image); - if (texture != null && SystemTitle.Image.Contains(@"@2x")) + Texture? texture = textureStore.Get(Image.Image); + if (texture != null && Image.Image.Contains(@"@2x")) texture.ScaleAdjust *= 2; AutoSizeAxes = Axes.Both; @@ -163,6 +152,19 @@ namespace osu.Game.Screens.Menu Blending = BlendingParameters.Additive, }, }; + + Action = () => + { + Flash(); + + // Delay slightly to allow animation to play out. + openUrlAction?.Cancel(); + openUrlAction = Scheduler.AddDelayed(() => + { + if (!string.IsNullOrEmpty(Image.Url)) + game?.HandleLink(Image.Url); + }, 250); + }; } protected override void LoadComplete() From 4c82e44291fdf23bf324d1d8f4a3092ede9437da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Mar 2024 20:07:17 +0800 Subject: [PATCH 099/581] Add isolated test coverage of online menu banner --- .../Visual/Menus/TestSceneMainMenu.cs | 23 ------ .../Visual/Menus/TestSceneOnlineMenuBanner.cs | 71 +++++++++++++++++++ 2 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 3c78edb8a5..e2a841d79a 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -32,29 +32,6 @@ namespace osu.Game.Tests.Visual.Menus AddAssert("system title not visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Hidden)); AddStep("enter menu", () => InputManager.Key(Key.Enter)); AddUntilStep("system title visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Visible)); - AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent - { - Images = new[] - { - new APIMenuImage - { - Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", - Url = @"https://osu.ppy.sh/community/contests/189", - } - } - }); - AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent - { - Images = new[] - { - new APIMenuImage - { - Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 - Url = @"https://osu.ppy.sh/community/contests/189", - } - } - }); - AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); } } } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs new file mode 100644 index 0000000000..a80212e0a1 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + public partial class TestSceneOnlineMenuBanner : OsuTestScene + { + private OnlineMenuBanner onlineMenuBanner = null!; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("Create banner", () => + { + Child = onlineMenuBanner = new OnlineMenuBanner + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = { Value = Visibility.Visible } + }; + }); + } + + [Test] + public void TestBasic() + { + AddAssert("system title not visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Hidden)); + AddStep("set online content", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + } + }, + }); + AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", + Url = @"https://osu.ppy.sh/community/contests/189", + } + } + }); + AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 + Url = @"https://osu.ppy.sh/community/contests/189", + } + } + }); + AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); + } + } +} From ec4a9a5fdd4ffa1df41b2e26227d27d1277ae8d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Mar 2024 23:07:31 +0800 Subject: [PATCH 100/581] Make work again for simple case --- .../API/Requests/Responses/APIMenuContent.cs | 12 ++++++--- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 25 +++++++------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs index acee6c99ba..7b53488030 100644 --- a/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs +++ b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using Newtonsoft.Json; namespace osu.Game.Online.API.Requests.Responses @@ -14,14 +15,12 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"images")] public APIMenuImage[] Images { get; init; } = Array.Empty(); - public DateTimeOffset LastUpdated { get; init; } - public bool Equals(APIMenuContent? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return LastUpdated.Equals(other.LastUpdated); + return Images.SequenceEqual(other.Images); } public override bool Equals(object? obj) @@ -36,7 +35,12 @@ namespace osu.Game.Online.API.Requests.Responses public override int GetHashCode() { - return LastUpdated.GetHashCode(); + var hash = new HashCode(); + + foreach (var image in Images) + hash.Add(image.GetHashCode()); + + return hash.ToHashCode(); } } } diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index cf20196f85..613a6eed4c 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Menu { base.LoadComplete(); - Current.BindValueChanged(_ => loadNewImage(), true); + Current.BindValueChanged(_ => loadNewImages(), true); checkForUpdates(); } @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Menu }); } - private void loadNewImage() + private void loadNewImages() { cancellationTokenSource?.Cancel(); cancellationTokenSource = null; @@ -131,19 +131,19 @@ namespace osu.Game.Screens.Menu public MenuImage(APIMenuImage image) { + AutoSizeAxes = Axes.Both; + Image = image; } [BackgroundDependencyLoader] - private void load(LargeTextureStore textureStore, OsuGame game) + private void load(LargeTextureStore textureStore, OsuGame? game) { Texture? texture = textureStore.Get(Image.Image); if (texture != null && Image.Image.Contains(@"@2x")) texture.ScaleAdjust *= 2; - AutoSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] + Children = new Drawable[] { new Sprite { Texture = texture }, flash = new Sprite @@ -155,7 +155,9 @@ namespace osu.Game.Screens.Menu Action = () => { - Flash(); + flash.FadeInFromZero(50) + .Then() + .FadeOut(500, Easing.OutQuint); // Delay slightly to allow animation to play out. openUrlAction?.Cancel(); @@ -174,15 +176,6 @@ namespace osu.Game.Screens.Menu this.FadeInFromZero(500, Easing.OutQuint); flash.FadeOutFromOne(4000, Easing.OutQuint); } - - public Drawable Flash() - { - flash.FadeInFromZero(50) - .Then() - .FadeOut(500, Easing.OutQuint); - - return this; - } } } } From a4c619ea97c190a9a4929d89268677af7b72a985 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Mar 2024 15:14:56 +0800 Subject: [PATCH 101/581] Add basic support for loading multiple images --- .../Visual/Menus/TestSceneOnlineMenuBanner.cs | 21 ++++- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 80 ++++++++++--------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index a80212e0a1..4cc379a18b 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs @@ -31,7 +31,6 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestBasic() { - AddAssert("system title not visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Hidden)); AddStep("set online content", () => onlineMenuBanner.Current.Value = new APIMenuContent { Images = new[] @@ -43,6 +42,7 @@ namespace osu.Game.Tests.Visual.Menus } }, }); + AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent { Images = new[] @@ -54,6 +54,24 @@ namespace osu.Game.Tests.Visual.Menus } } }); + + AddStep("set multiple images", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + }, + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", + Url = @"https://osu.ppy.sh/community/contests/189", + } + }, + }); + AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent { Images = new[] @@ -65,6 +83,7 @@ namespace osu.Game.Tests.Visual.Menus } } }); + AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); } } diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 613a6eed4c..587a93fb67 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Menu private Container content = null!; private CancellationTokenSource? cancellationTokenSource; - private MenuImage? currentImage; [BackgroundDependencyLoader] private void load() @@ -48,32 +47,6 @@ namespace osu.Game.Screens.Menu protected override void PopOut() => content.FadeOut(transition_duration, Easing.OutQuint); - protected override bool OnHover(HoverEvent e) - { - content.ScaleTo(1.05f, 2000, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - content.ScaleTo(1f, 500, Easing.OutQuint); - base.OnHoverLost(e); - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - content.ScaleTo(0.95f, 500, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - content - .ScaleTo(0.95f) - .ScaleTo(1, 500, Easing.OutElastic); - base.OnMouseUp(e); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -106,17 +79,26 @@ namespace osu.Game.Screens.Menu { cancellationTokenSource?.Cancel(); cancellationTokenSource = null; - currentImage?.FadeOut(500, Easing.OutQuint).Expire(); - if (Current.Value.Images.Length == 0) + var newContent = Current.Value; + + foreach (var i in content) + { + i.FadeOutFromOne(100, Easing.OutQuint) + .Expire(); + } + + if (newContent.Images.Length == 0) return; - LoadComponentAsync(new MenuImage(Current.Value.Images.First()), loaded => + LoadComponentsAsync(newContent.Images.Select(i => new MenuImage(i)), loaded => { - if (!loaded.Image.Equals(Current.Value.Images.First())) - loaded.Dispose(); + if (!newContent.Equals(Current.Value)) + return; - content.Add(currentImage = loaded); + content.AddRange(loaded); + + loaded.First().Show(); }, (cancellationTokenSource ??= new CancellationTokenSource()).Token); } @@ -132,6 +114,8 @@ namespace osu.Game.Screens.Menu public MenuImage(APIMenuImage image) { AutoSizeAxes = Axes.Both; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; Image = image; } @@ -169,13 +153,37 @@ namespace osu.Game.Screens.Menu }; } - protected override void LoadComplete() + public override void Show() { - base.LoadComplete(); - this.FadeInFromZero(500, Easing.OutQuint); flash.FadeOutFromOne(4000, Easing.OutQuint); } + + protected override bool OnHover(HoverEvent e) + { + this.ScaleTo(1.05f, 2000, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + this.ScaleTo(1f, 500, Easing.OutQuint); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + this.ScaleTo(0.95f, 500, Easing.OutQuint); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + this + .ScaleTo(0.95f) + .ScaleTo(1, 500, Easing.OutElastic); + base.OnMouseUp(e); + } } } } From d0b164b44f6d986f965c469cd838bfacbe0a3de6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Mar 2024 23:37:30 +0800 Subject: [PATCH 102/581] Add automatic rotation support --- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 38 +++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 587a93fb67..a4648265ae 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -28,6 +28,10 @@ namespace osu.Game.Screens.Menu private Container content = null!; private CancellationTokenSource? cancellationTokenSource; + private int displayIndex = -1; + + private ScheduledDelegate? nextDisplay; + [BackgroundDependencyLoader] private void load() { @@ -77,16 +81,16 @@ namespace osu.Game.Screens.Menu private void loadNewImages() { + nextDisplay?.Cancel(); + cancellationTokenSource?.Cancel(); cancellationTokenSource = null; var newContent = Current.Value; - foreach (var i in content) - { - i.FadeOutFromOne(100, Easing.OutQuint) - .Expire(); - } + // A better fade out would be nice, but the menu content changes *very* rarely + // so let's keep things simple for now. + content.Clear(true); if (newContent.Images.Length == 0) return; @@ -96,12 +100,34 @@ namespace osu.Game.Screens.Menu if (!newContent.Equals(Current.Value)) return; + // start hidden + foreach (var image in loaded) + image.Hide(); + content.AddRange(loaded); - loaded.First().Show(); + displayIndex = -1; + showNext(); }, (cancellationTokenSource ??= new CancellationTokenSource()).Token); } + private void showNext() + { + nextDisplay?.Cancel(); + + bool previousShowing = displayIndex >= 0; + if (previousShowing) + content[displayIndex % content.Count].FadeOut(400, Easing.OutQuint); + + displayIndex++; + + using (BeginDelayedSequence(previousShowing ? 300 : 0)) + content[displayIndex % content.Count].Show(); + + if (content.Count > 1) + nextDisplay = Scheduler.AddDelayed(showNext, 12000); + } + [LongRunningLoad] private partial class MenuImage : OsuClickableContainer { From 3847aae57d88ea2156c7599c53a0e994fffd4760 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Mar 2024 12:14:40 +0800 Subject: [PATCH 103/581] Don't rotate when hovering --- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 26 ++++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index a4648265ae..74062d5b9f 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -21,6 +21,8 @@ namespace osu.Game.Screens.Menu { public partial class OnlineMenuBanner : VisibilityContainer { + public double DelayBetweenRotation = 7500; + internal Bindable Current { get; } = new Bindable(new APIMenuContent()); private const float transition_duration = 500; @@ -115,21 +117,29 @@ namespace osu.Game.Screens.Menu { nextDisplay?.Cancel(); - bool previousShowing = displayIndex >= 0; - if (previousShowing) - content[displayIndex % content.Count].FadeOut(400, Easing.OutQuint); + // If the user is hovering a banner, don't rotate yet. + bool anyHovered = content.Any(i => i.IsHovered); - displayIndex++; + if (!anyHovered) + { + bool previousShowing = displayIndex >= 0; + if (previousShowing) + content[displayIndex % content.Count].FadeOut(400, Easing.OutQuint); - using (BeginDelayedSequence(previousShowing ? 300 : 0)) - content[displayIndex % content.Count].Show(); + displayIndex++; + + using (BeginDelayedSequence(previousShowing ? 300 : 0)) + content[displayIndex % content.Count].Show(); + } if (content.Count > 1) - nextDisplay = Scheduler.AddDelayed(showNext, 12000); + { + nextDisplay = Scheduler.AddDelayed(showNext, DelayBetweenRotation); + } } [LongRunningLoad] - private partial class MenuImage : OsuClickableContainer + public partial class MenuImage : OsuClickableContainer { public readonly APIMenuImage Image; From e9f15534ed7ea64b7cfe77360f488b3a63e703db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Mar 2024 12:14:47 +0800 Subject: [PATCH 104/581] Improve test coverage --- .../Visual/Menus/TestSceneOnlineMenuBanner.cs | 75 ++++++++++++++++--- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 7 +- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index 4cc379a18b..6be5b80983 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.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 System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -21,6 +22,8 @@ namespace osu.Game.Tests.Visual.Menus { Child = onlineMenuBanner = new OnlineMenuBanner { + FetchOnlineContent = false, + DelayBetweenRotation = 500, Anchor = Anchor.Centre, Origin = Anchor.Centre, State = { Value = Visibility.Visible } @@ -43,6 +46,18 @@ namespace osu.Game.Tests.Visual.Menus }, }); + AddUntilStep("wait for one image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 1) + return false; + + var image = images.Single(); + + return image.IsPresent && image.Image.Url == "https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023"; + }); + AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent { Images = new[] @@ -55,6 +70,40 @@ namespace osu.Game.Tests.Visual.Menus } }); + AddUntilStep("wait for new image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 1) + return false; + + var image = images.Single(); + + return image.IsPresent && image.Image.Url == "https://osu.ppy.sh/community/contests/189"; + }); + + AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 + Url = @"https://osu.ppy.sh/community/contests/189", + } + } + }); + + AddUntilStep("wait for no image shown", () => !onlineMenuBanner.ChildrenOfType().Any()); + + AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); + + AddUntilStep("wait for no image shown", () => !onlineMenuBanner.ChildrenOfType().Any()); + } + + [Test] + public void TestMultipleImages() + { AddStep("set multiple images", () => onlineMenuBanner.Current.Value = new APIMenuContent { Images = new[] @@ -72,19 +121,25 @@ namespace osu.Game.Tests.Visual.Menus }, }); - AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent + AddUntilStep("wait for first image shown", () => { - Images = new[] - { - new APIMenuImage - { - Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 - Url = @"https://osu.ppy.sh/community/contests/189", - } - } + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 2) + return false; + + return images.First().IsPresent && !images.Last().IsPresent; }); - AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); + AddUntilStep("wait for second image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 2) + return false; + + return !images.First().IsPresent && images.Last().IsPresent; + }); } } } diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 74062d5b9f..2ab6417370 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -21,7 +21,9 @@ namespace osu.Game.Screens.Menu { public partial class OnlineMenuBanner : VisibilityContainer { - public double DelayBetweenRotation = 7500; + public double DelayBetweenRotation { get; set; } = 7500; + + public bool FetchOnlineContent { get; set; } = true; internal Bindable Current { get; } = new Bindable(new APIMenuContent()); @@ -64,6 +66,9 @@ namespace osu.Game.Screens.Menu private void checkForUpdates() { + if (!FetchOnlineContent) + return; + var request = new GetMenuContentRequest(); Task.Run(() => request.Perform()) .ContinueWith(r => From f0614928b182bf2d575c5e298e29d2254fd28ecb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Mar 2024 13:19:12 +0800 Subject: [PATCH 105/581] Read from new location --- osu.Game/Online/API/Requests/GetMenuContentRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetMenuContentRequest.cs b/osu.Game/Online/API/Requests/GetMenuContentRequest.cs index ad2bac6696..26747489d6 100644 --- a/osu.Game/Online/API/Requests/GetMenuContentRequest.cs +++ b/osu.Game/Online/API/Requests/GetMenuContentRequest.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.API.Requests public class GetMenuContentRequest : OsuJsonWebRequest { public GetMenuContentRequest() - : base(@"https://assets.ppy.sh/lazer-status.json") + : base(@"https://assets.ppy.sh/menu-content.json") { } } From 057f86dd145dd5d0ba573737ca61aceb27dcc70a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Mar 2024 14:28:23 +0800 Subject: [PATCH 106/581] Add handling of expiration --- .../Visual/Menus/TestSceneOnlineMenuBanner.cs | 60 +++++++++++++++++++ .../API/Requests/Responses/APIMenuImage.cs | 4 ++ osu.Game/Screens/Menu/OnlineMenuBanner.cs | 60 ++++++++++++------- 3 files changed, 102 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index 6be5b80983..2dd08ce306 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.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 System; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; @@ -141,5 +142,64 @@ namespace osu.Game.Tests.Visual.Menus return !images.First().IsPresent && images.Last().IsPresent; }); } + + [Test] + public void TestExpiry() + { + AddStep("set multiple images, second expiring soon", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + }, + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", + Url = @"https://osu.ppy.sh/community/contests/189", + Expires = DateTimeOffset.Now.AddSeconds(2), + } + }, + }); + + AddUntilStep("wait for first image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 2) + return false; + + return images.First().IsPresent && !images.Last().IsPresent; + }); + + AddUntilStep("wait for second image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 2) + return false; + + return !images.First().IsPresent && images.Last().IsPresent; + }); + + AddUntilStep("wait for expiry", () => + { + return onlineMenuBanner + .ChildrenOfType() + .Any(i => !i.Image.IsCurrent); + }); + + AddUntilStep("wait for first image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 2) + return false; + + return images.First().IsPresent && !images.Last().IsPresent; + }); + } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs index 4824e23d4b..42129ca96e 100644 --- a/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs +++ b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs @@ -20,6 +20,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"url")] public string Url { get; init; } = string.Empty; + public bool IsCurrent => + (Begins == null || Begins < DateTimeOffset.UtcNow) && + (Expires == null || Expires > DateTimeOffset.UtcNow); + /// /// The time at which this item should begin displaying. If null, will display immediately. /// diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 2ab6417370..55ceb84d7b 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Menu private const float transition_duration = 500; - private Container content = null!; + private Container content = null!; private CancellationTokenSource? cancellationTokenSource; private int displayIndex = -1; @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Menu AutoSizeDuration = transition_duration; AutoSizeEasing = Easing.OutQuint; - InternalChild = content = new Container + InternalChild = content = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -59,8 +59,7 @@ namespace osu.Game.Screens.Menu { base.LoadComplete(); - Current.BindValueChanged(_ => loadNewImages(), true); - + Current.BindValueChanged(loadNewImages, true); checkForUpdates(); } @@ -86,25 +85,24 @@ namespace osu.Game.Screens.Menu }); } - private void loadNewImages() + /// + /// Takes and materialises and displays drawables for all valid images to be displayed. + /// + /// + private void loadNewImages(ValueChangedEvent images) { nextDisplay?.Cancel(); cancellationTokenSource?.Cancel(); cancellationTokenSource = null; - var newContent = Current.Value; - // A better fade out would be nice, but the menu content changes *very* rarely // so let's keep things simple for now. content.Clear(true); - if (newContent.Images.Length == 0) - return; - - LoadComponentsAsync(newContent.Images.Select(i => new MenuImage(i)), loaded => + LoadComponentsAsync(images.NewValue.Images.Select(i => new MenuImage(i)), loaded => { - if (!newContent.Equals(Current.Value)) + if (!images.NewValue.Equals(Current.Value)) return; // start hidden @@ -127,20 +125,38 @@ namespace osu.Game.Screens.Menu if (!anyHovered) { - bool previousShowing = displayIndex >= 0; - if (previousShowing) - content[displayIndex % content.Count].FadeOut(400, Easing.OutQuint); + int previousIndex = displayIndex; - displayIndex++; + if (displayIndex == -1) + displayIndex = 0; - using (BeginDelayedSequence(previousShowing ? 300 : 0)) - content[displayIndex % content.Count].Show(); + // To handle expiration simply, arrange all images in best-next order. + // Fade in the first valid one, then handle fading out the last if required. + var currentRotation = content + .Skip(displayIndex + 1) + .Concat(content.Take(displayIndex + 1)); + + foreach (var image in currentRotation) + { + if (!image.Image.IsCurrent) continue; + + using (BeginDelayedSequence(previousIndex >= 0 ? 300 : 0)) + { + displayIndex = content.IndexOf(image); + + if (displayIndex != previousIndex) + image.Show(); + + break; + } + } + + if (previousIndex >= 0 && previousIndex != displayIndex) + content[previousIndex].FadeOut(400, Easing.OutQuint); } - if (content.Count > 1) - { - nextDisplay = Scheduler.AddDelayed(showNext, DelayBetweenRotation); - } + // Re-scheduling this method will both handle rotation and re-checking for expiration dates. + nextDisplay = Scheduler.AddDelayed(showNext, DelayBetweenRotation); } [LongRunningLoad] From bb9fa52fda51f46e4e0fe39e2be8196ab0dc0fba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Mar 2024 14:53:05 +0800 Subject: [PATCH 107/581] Fix `displayIndex` not being correctly set to `-1` after last expiry date --- .../Visual/Menus/TestSceneOnlineMenuBanner.cs | 36 ++++++++++++++++++- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 11 +++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index 2dd08ce306..380085ce04 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs @@ -144,7 +144,41 @@ namespace osu.Game.Tests.Visual.Menus } [Test] - public void TestExpiry() + public void TestFutureSingle() + { + AddStep("set image with time constraints", () => onlineMenuBanner.Current.Value = new APIMenuContent + { + Images = new[] + { + new APIMenuImage + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + Begins = DateTimeOffset.Now.AddSeconds(2), + Expires = DateTimeOffset.Now.AddSeconds(5), + }, + }, + }); + + AddUntilStep("wait for no image shown", () => !onlineMenuBanner.ChildrenOfType().Any(i => i.IsPresent)); + + AddUntilStep("wait for one image shown", () => + { + var images = onlineMenuBanner.ChildrenOfType(); + + if (images.Count() != 1) + return false; + + var image = images.Single(); + + return image.IsPresent && image.Image.Url == "https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023"; + }); + + AddUntilStep("wait for no image shown", () => !onlineMenuBanner.ChildrenOfType().Any(i => i.IsPresent)); + } + + [Test] + public void TestExpiryMultiple() { AddStep("set multiple images, second expiring soon", () => onlineMenuBanner.Current.Value = new APIMenuContent { diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 55ceb84d7b..37bec7aa63 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -127,14 +127,15 @@ namespace osu.Game.Screens.Menu { int previousIndex = displayIndex; - if (displayIndex == -1) - displayIndex = 0; - // To handle expiration simply, arrange all images in best-next order. // Fade in the first valid one, then handle fading out the last if required. var currentRotation = content - .Skip(displayIndex + 1) - .Concat(content.Take(displayIndex + 1)); + .Skip(Math.Max(0, previousIndex) + 1) + .Concat(content.Take(Math.Max(0, previousIndex) + 1)); + + // After the loop, displayIndex will be the new valid index or -1 if + // none valid. + displayIndex = -1; foreach (var image in currentRotation) { From 78037fa4773dfc8f8063d45e104bd84131515bcc Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Mon, 25 Mar 2024 04:19:14 -0300 Subject: [PATCH 108/581] Handle new combo on `HandleReverse` --- .../Edit/CatchSelectionHandler.cs | 33 ++++++++++++++++--- .../Edit/OsuSelectionHandler.cs | 20 +++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 418351e2f3..e3d82cc517 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -76,21 +76,44 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleReverse() { + var hitObjects = EditorBeatmap.SelectedHitObjects; + double selectionStartTime = SelectedItems.Min(h => h.StartTime); double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); - EditorBeatmap.PerformOnSelection(hitObject => - { - hitObject.StartTime = selectionEndTime - (hitObject.GetEndTime() - selectionStartTime); + var newComboPlaces = hitObjects + .OfType() + .Where(h => h.NewCombo) + .Select(obj => obj.StartTime) + .ToList(); - if (hitObject is JuiceStream juiceStream) + foreach (var h in hitObjects) + { + h.StartTime = selectionEndTime - (h.GetEndTime() - selectionStartTime); + + if (h is JuiceStream juiceStream) { juiceStream.Path.Reverse(out Vector2 positionalOffset); juiceStream.OriginalX += positionalOffset.X; juiceStream.LegacyConvertedY += positionalOffset.Y; EditorBeatmap.Update(juiceStream); } - }); + } + + foreach (var h in hitObjects) + { + if (h is CatchHitObject obj) obj.NewCombo = false; + } + + foreach (double place in newComboPlaces) + { + hitObjects + .OfType() + .Where(obj => obj.StartTime == place) + .ToList() + .ForEach(obj => obj.NewCombo = true); + } + return true; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index cea2adc6e2..b4980b55d4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -85,6 +85,12 @@ namespace osu.Game.Rulesets.Osu.Edit bool moreThanOneObject = hitObjects.Count > 1; + var newComboPlaces = hitObjects + .OfType() + .Where(h => h.NewCombo) + .Select(obj => obj.StartTime) + .ToList(); + foreach (var h in hitObjects) { if (moreThanOneObject) @@ -97,6 +103,20 @@ namespace osu.Game.Rulesets.Osu.Edit } } + foreach (var h in hitObjects) + { + if (h is OsuHitObject obj) obj.NewCombo = false; + } + + foreach (double place in newComboPlaces) + { + hitObjects + .OfType() + .Where(obj => obj.StartTime == place) + .ToList() + .ForEach(obj => obj.NewCombo = true); + } + return true; } From fb08d6816ba21cc6cb3351694ff587a2100fc86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 11:33:15 +0100 Subject: [PATCH 109/581] Only attempt to disable rulesets when decidedly crashing out --- osu.Game/OsuGameBase.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8bda8fb6c2..fb7a238c46 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -678,18 +678,21 @@ namespace osu.Game /// /// Allows a maximum of one unhandled exception, per second of execution. /// + /// Whether to ignore the exception and continue running. private bool onExceptionThrown(Exception ex) { - bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; - - Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); - - RulesetStore.TryDisableCustomRulesetsCausing(ex); + if (Interlocked.Decrement(ref allowableExceptions) < 0) + { + Logger.Log("Too many unhandled exceptions, crashing out."); + RulesetStore.TryDisableCustomRulesetsCausing(ex); + return false; + } + Logger.Log($"Unhandled exception has been allowed with {allowableExceptions} more allowable exceptions."); // restore the stock of allowable exceptions after a short delay. Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); - return continueExecution; + return true; } protected override void Dispose(bool isDisposing) From 4979305b2d59188e97efd03e4e49e90aacb32485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 11:34:29 +0100 Subject: [PATCH 110/581] Ensure `TryDisableCustomRulesetsCausing()` never actually crashes itself --- osu.Game/Rulesets/RealmRulesetStore.cs | 31 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/RealmRulesetStore.cs b/osu.Game/Rulesets/RealmRulesetStore.cs index 36eae7af2c..2455a9a73f 100644 --- a/osu.Game/Rulesets/RealmRulesetStore.cs +++ b/osu.Game/Rulesets/RealmRulesetStore.cs @@ -164,26 +164,33 @@ namespace osu.Game.Rulesets internal void TryDisableCustomRulesetsCausing(Exception exception) { - var stackTrace = new StackTrace(exception); - - foreach (var frame in stackTrace.GetFrames()) + try { - var declaringAssembly = frame.GetMethod()?.DeclaringType?.Assembly; - if (declaringAssembly == null) - continue; + var stackTrace = new StackTrace(exception); - if (UserRulesetAssemblies.Contains(declaringAssembly)) + foreach (var frame in stackTrace.GetFrames()) { - string sourceLocation = declaringAssembly.Location; - string destinationLocation = Path.ChangeExtension(sourceLocation, @".dll.broken"); + var declaringAssembly = frame.GetMethod()?.DeclaringType?.Assembly; + if (declaringAssembly == null) + continue; - if (File.Exists(sourceLocation)) + if (UserRulesetAssemblies.Contains(declaringAssembly)) { - Logger.Log($"Unhandled exception traced back to custom ruleset {Path.GetFileNameWithoutExtension(sourceLocation)}. Marking as broken."); - File.Move(sourceLocation, destinationLocation); + string sourceLocation = declaringAssembly.Location; + string destinationLocation = Path.ChangeExtension(sourceLocation, @".dll.broken"); + + if (File.Exists(sourceLocation)) + { + Logger.Log($"Unhandled exception traced back to custom ruleset {Path.GetFileNameWithoutExtension(sourceLocation)}. Marking as broken."); + File.Move(sourceLocation, destinationLocation); + } } } } + catch (Exception ex) + { + Logger.Log($"Attempt to trace back crash to custom ruleset failed: {ex}"); + } } } } From 3db88fbcea288f5c4136739bd26e0bdab9e0b05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 17:54:20 +0100 Subject: [PATCH 111/581] Use less confusing message format when logging discord errors The "code" is a number, so it looked weird when put in the middle without any nearby punctuation. Example: An error occurred with Discord RPC Client: 5005 secrets cannot currently be sent with buttons --- osu.Desktop/DiscordRichPresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index d78459ff28..eaa9a90f27 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -75,7 +75,7 @@ namespace osu.Desktop }; client.OnReady += onReady; - client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network, LogLevel.Error); + client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Message} ({e.Code})", LoggingTarget.Network, LogLevel.Error); // A URI scheme is required to support game invitations, as well as informing Discord of the game executable path to support launching the game when a user clicks on join/spectate. client.RegisterUriScheme(); From e95f29cf4ba8840e0305bbae7637d01ddc03f285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 17:57:13 +0100 Subject: [PATCH 112/581] Rename `updatePresence() => schedulePresenceUpdate()` The method doesn't actually update anything by itself, and I want to free up the `updatePresence()` name for the actual update. --- osu.Desktop/DiscordRichPresence.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index eaa9a90f27..811f2f3548 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -94,10 +94,10 @@ namespace osu.Desktop activity.BindTo(u.NewValue.Activity); }, true); - ruleset.BindValueChanged(_ => updatePresence()); - status.BindValueChanged(_ => updatePresence()); - activity.BindValueChanged(_ => updatePresence()); - privacyMode.BindValueChanged(_ => updatePresence()); + ruleset.BindValueChanged(_ => schedulePresenceUpdate()); + status.BindValueChanged(_ => schedulePresenceUpdate()); + activity.BindValueChanged(_ => schedulePresenceUpdate()); + privacyMode.BindValueChanged(_ => schedulePresenceUpdate()); multiplayerClient.RoomUpdated += onRoomUpdated; client.Initialize(); @@ -111,14 +111,14 @@ namespace osu.Desktop if (client.CurrentPresence != null) client.SetPresence(null); - updatePresence(); + schedulePresenceUpdate(); } - private void onRoomUpdated() => updatePresence(); + private void onRoomUpdated() => schedulePresenceUpdate(); private ScheduledDelegate? presenceUpdateDelegate; - private void updatePresence() + private void schedulePresenceUpdate() { presenceUpdateDelegate?.Cancel(); presenceUpdateDelegate = Scheduler.AddDelayed(() => From a398754a27ac26a1f6e57790b2c609226ad805fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 18:00:42 +0100 Subject: [PATCH 113/581] Merge all presence methods into one I'm about to make them interdependent (and it's discord's fault), so it doesn't really make sense to make them separate at this point I don't think. And it felt weird anyway. --- osu.Desktop/DiscordRichPresence.cs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 811f2f3548..d67437ba62 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -134,16 +134,14 @@ namespace osu.Desktop bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited || status.Value == UserStatus.DoNotDisturb; - updatePresenceStatus(hideIdentifiableInformation); - updatePresenceParty(hideIdentifiableInformation); - updatePresenceAssets(); - + updatePresence(hideIdentifiableInformation); client.SetPresence(presence); }, 200); } - private void updatePresenceStatus(bool hideIdentifiableInformation) + private void updatePresence(bool hideIdentifiableInformation) { + // user activity if (activity.Value != null) { presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation)); @@ -170,10 +168,8 @@ namespace osu.Desktop presence.State = "Idle"; presence.Details = string.Empty; } - } - private void updatePresenceParty(bool hideIdentifiableInformation) - { + // user party if (!hideIdentifiableInformation && multiplayerClient.Room != null) { MultiplayerRoom room = multiplayerClient.Room; @@ -201,11 +197,9 @@ namespace osu.Desktop presence.Party = null; presence.Secrets.JoinSecret = null; } - } - private void updatePresenceAssets() - { - // update user information + // game images: + // large image tooltip if (privacyMode.Value == DiscordRichPresenceMode.Limited) presence.Assets.LargeImageText = string.Empty; else @@ -216,7 +210,7 @@ namespace osu.Desktop presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty); } - // update ruleset + // small image presence.Assets.SmallImageKey = ruleset.Value.IsLegacyRuleset() ? $"mode_{ruleset.Value.OnlineID}" : "mode_custom"; presence.Assets.SmallImageText = ruleset.Value.Name; } From 53c3aec3c364a2541180e1bc34b4c40ee688f2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 18:02:54 +0100 Subject: [PATCH 114/581] Fix discord RPC errors in multiplayer Reproduction steps: 1. Go to multiplayer 2. Create a room 3. Play a map to completion 4. Wait for "secrets cannot currently be sent with buttons" error messages The fix is to clear the buttons since they're the less important ones. --- osu.Desktop/DiscordRichPresence.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index d67437ba62..6e8554d617 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -191,6 +191,9 @@ namespace osu.Desktop }; presence.Secrets.JoinSecret = JsonConvert.SerializeObject(roomSecret); + // discord cannot handle both secrets and buttons at the same time, so we need to choose something. + // the multiplayer room seems more important. + presence.Buttons = null; } else { From 6266af8a5696edb32da2f57b877f84dbcab66e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 18:57:58 +0100 Subject: [PATCH 115/581] Fix taiko legacy score simulator not including swell tick score gain into bonus portion Reported in https://discord.com/channels/188630481301012481/1097318920991559880/1221836384038551613. Example score: https://osu.ppy.sh/scores/1855965185 The cause of the overestimation was an error in taiko's score simulator. In lazer taiko, swell ticks don't give any score anymore, while they did in stable. For all intents and purposes, swell ticks can be considered "bonus" objects that "don't give any actual bonus score". Which is to say, during simulation of a legacy score swell ticks hit should be treated as bonus, because if they aren't, then otherwise they will be treated essentially as *normal hits*, meaning that they will be included in the *accuracy* portion of score, which breaks all sorts of follow-up assumptions: - The accuracy portion of the best possible total score becomes overinflated in comparison to reality, while the combo portion of that maximum score becomes underestimated. - Because the affected score has low accuracy, the estimated accuracy portion of the score (as given by maximmum accuracy portion of score times the actual numerical accuracy of the score) is also low. - However, the next step is estimating the combo portion, which is done by taking legacy total score, subtracting the aforementioned estimation for accuracy total score from that, and then dividing the result by the maximum achievable combo score on the map. Because most of actual "combo" score from swell ticks was "moved" into the accuracy portion due to the aforementioned error, the maximum achievable combo score becomes so small that the estimated combo portion exceeds 1. Instead, this change makes it so that gains from swell ticks are treated as "bonus", which means that they are excluded from the accuracy portion of score and instead count into the bonus portion of score, bringing the scores concerned more in line with expectations - although due to pessimistic assumptions in the simulation of the swell itself, the conversion will still overestimate total score for affected scores, just not by *that* much. --- osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index 66ff0fc3d9..9839d94277 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty case SwellTick: scoreIncrease = 300; increaseCombo = false; + isBonus = true; + bonusResult = HitResult.IgnoreHit; break; case DrumRollTick: From 73926592b91e1cf6348b3e7ae3c68142dbbd8d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Mar 2024 19:27:38 +0100 Subject: [PATCH 116/581] Bump legacy score version to recalculate all scores --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 93f51ee74d..0f00cce080 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -46,9 +46,10 @@ namespace osu.Game.Scoring.Legacy /// 30000013: All local scores will use lazer definitions of ranks for consistency. Recalculates the rank of all scores. /// 30000014: Fix edge cases in conversion for osu! scores on selected beatmaps. Reconvert all scores. /// 30000015: Fix osu! standardised score estimation algorithm violating basic invariants. Reconvert all scores. + /// 30000016: Fix taiko standardised score estimation algorithm not including swell tick score gain into bonus portion. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000015; + public const int LATEST_VERSION = 30000016; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 10683de578590d9a17c690df4ef9fc89515599a5 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 26 Mar 2024 04:59:47 -0300 Subject: [PATCH 117/581] Use order of new combo flags instead of `StartTime` --- .../Edit/CatchSelectionHandler.cs | 22 +++++++------------ .../Edit/OsuSelectionHandler.cs | 22 +++++++------------ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index e3d82cc517..e3d2347c4d 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -81,12 +81,13 @@ namespace osu.Game.Rulesets.Catch.Edit double selectionStartTime = SelectedItems.Min(h => h.StartTime); double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); - var newComboPlaces = hitObjects + var newComboOrder = hitObjects .OfType() - .Where(h => h.NewCombo) - .Select(obj => obj.StartTime) + .Select(obj => obj.NewCombo) .ToList(); + newComboOrder.Reverse(); + foreach (var h in hitObjects) { h.StartTime = selectionEndTime - (h.GetEndTime() - selectionStartTime); @@ -100,18 +101,11 @@ namespace osu.Game.Rulesets.Catch.Edit } } - foreach (var h in hitObjects) + int i = 0; + foreach (bool newCombo in newComboOrder) { - if (h is CatchHitObject obj) obj.NewCombo = false; - } - - foreach (double place in newComboPlaces) - { - hitObjects - .OfType() - .Where(obj => obj.StartTime == place) - .ToList() - .ForEach(obj => obj.NewCombo = true); + hitObjects.OfType().ToList()[i].NewCombo = newCombo; + i++; } return true; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index b4980b55d4..df39ab8fce 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -85,12 +85,13 @@ namespace osu.Game.Rulesets.Osu.Edit bool moreThanOneObject = hitObjects.Count > 1; - var newComboPlaces = hitObjects + var newComboOrder = hitObjects .OfType() - .Where(h => h.NewCombo) - .Select(obj => obj.StartTime) + .Select(obj => obj.NewCombo) .ToList(); + newComboOrder.Reverse(); + foreach (var h in hitObjects) { if (moreThanOneObject) @@ -103,18 +104,11 @@ namespace osu.Game.Rulesets.Osu.Edit } } - foreach (var h in hitObjects) + int i = 0; + foreach (bool newCombo in newComboOrder) { - if (h is OsuHitObject obj) obj.NewCombo = false; - } - - foreach (double place in newComboPlaces) - { - hitObjects - .OfType() - .Where(obj => obj.StartTime == place) - .ToList() - .ForEach(obj => obj.NewCombo = true); + hitObjects.OfType().ToList()[i].NewCombo = newCombo; + i++; } return true; From 9b923b19094bbcdf3f130d005085de70368cb8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 10:55:49 +0100 Subject: [PATCH 118/581] Fix code quality issues --- .../Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs | 4 ++-- .../Editor/Checks/CheckKeyCountTest.cs | 2 +- .../Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs | 4 ++-- .../Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs | 4 ++-- .../Edit/Checks/CheckOsuAbnormalDifficultySettings.cs | 1 + .../Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs | 4 ++-- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs index 2ae2e20215..33aa4cba5d 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/Checks/CheckCatchAbnormalDifficultySettingsTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor.Checks { private CheckCatchAbnormalDifficultySettings check = null!; - private IBeatmap beatmap = new Beatmap(); + private readonly IBeatmap beatmap = new Beatmap(); [SetUp] public void Setup() @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor.Checks check = new CheckCatchAbnormalDifficultySettings(); beatmap.BeatmapInfo.Ruleset = new CatchRuleset().RulesetInfo; - beatmap.Difficulty = new BeatmapDifficulty() + beatmap.Difficulty = new BeatmapDifficulty { ApproachRate = 5, CircleSize = 5, diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs index 564c611548..b40a62176c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckKeyCountTest.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks { check = new CheckKeyCount(); - beatmap = new Beatmap() + beatmap = new Beatmap { BeatmapInfo = new BeatmapInfo { diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs index 6c585aace3..da5ab037e5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/Checks/CheckManiaAbnormalDifficultySettingsTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks { private CheckManiaAbnormalDifficultySettings check = null!; - private IBeatmap beatmap = new Beatmap(); + private readonly IBeatmap beatmap = new Beatmap(); [SetUp] public void Setup() @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor.Checks check = new CheckManiaAbnormalDifficultySettings(); beatmap.BeatmapInfo.Ruleset = new ManiaRuleset().RulesetInfo; - beatmap.Difficulty = new BeatmapDifficulty() + beatmap.Difficulty = new BeatmapDifficulty { OverallDifficulty = 5, DrainRate = 5, diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs index 53ccd3c7a7..5f49714d93 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOsuAbnormalDifficultySettingsTest.cs @@ -17,14 +17,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks { private CheckOsuAbnormalDifficultySettings check = null!; - private IBeatmap beatmap = new Beatmap(); + private readonly IBeatmap beatmap = new Beatmap(); [SetUp] public void Setup() { check = new CheckOsuAbnormalDifficultySettings(); - beatmap.Difficulty = new BeatmapDifficulty() + beatmap.Difficulty = new BeatmapDifficulty { ApproachRate = 5, CircleSize = 5, diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs index c1eca7fff7..7ad861f317 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs @@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public class CheckOsuAbnormalDifficultySettings : CheckAbnormalDifficultySettings { public override CheckMetadata Metadata => new CheckMetadata(CheckCategory.Settings, "Checks osu relevant settings"); + public override IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs index f10e62f3bf..4a6cf0313a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor.Checks { private CheckTaikoAbnormalDifficultySettings check = null!; - private IBeatmap beatmap = new Beatmap(); + private readonly IBeatmap beatmap = new Beatmap(); [SetUp] public void Setup() @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor.Checks check = new CheckTaikoAbnormalDifficultySettings(); beatmap.BeatmapInfo.Ruleset = new TaikoRuleset().RulesetInfo; - beatmap.Difficulty = new BeatmapDifficulty() + beatmap.Difficulty = new BeatmapDifficulty { OverallDifficulty = 5, }; From 8fb308c1925e1cdbda3eba6e58b7ef5c8fb1fe89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 10:57:20 +0100 Subject: [PATCH 119/581] Add failing test coverage for checking taiko HP too I was wrong, taiko uses HP (to calculate miss penalty). --- ...heckTaikoAbnormalDifficultySettingsTest.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs index 4a6cf0313a..6a50fd0956 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/Checks/CheckTaikoAbnormalDifficultySettingsTest.cs @@ -52,6 +52,18 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor.Checks Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); } + [Test] + public void TestDrainRateTwoDecimals() + { + beatmap.Difficulty.DrainRate = 5.55f; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateMoreThanOneDecimal); + } + [Test] public void TestOverallDifficultyUnder() { @@ -76,6 +88,30 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor.Checks Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); } + [Test] + public void TestDrainRateUnder() + { + beatmap.Difficulty.DrainRate = -10; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + + [Test] + public void TestDrainRateOver() + { + beatmap.Difficulty.DrainRate = 20; + + var context = getContext(); + var issues = check.Run(context).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckAbnormalDifficultySettings.IssueTemplateOutOfRange); + } + private BeatmapVerifierContext getContext() { return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); From e7cf1ab4df36879306ef6e4c38b535ba370a0e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 10:58:39 +0100 Subject: [PATCH 120/581] Add checks for taiko drain rate --- .../Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs index ce35f21853..10e2867ca0 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs @@ -21,6 +21,12 @@ namespace osu.Game.Rulesets.Taiko.Edit.Checks if (OutOfRange(diff.OverallDifficulty)) yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + + if (HasMoreThanOneDecimalPlace(diff.DrainRate)) + yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + + if (OutOfRange(diff.DrainRate)) + yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); } } } From 1866b4b6b10d1990db311eb96ae02c5c5609e918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 11:13:03 +0100 Subject: [PATCH 121/581] Refactor abstract check to reduce duplication --- .../CheckCatchAbnormalDifficultySettings.cs | 25 +++++++------- .../CheckManiaAbnormalDifficultySettings.cs | 17 +++++----- .../CheckOsuAbnormalDifficultySettings.cs | 33 ++++++++++--------- .../CheckTaikoAbnormalDifficultySettings.cs | 17 +++++----- .../Checks/CheckAbnormalDifficultySettings.cs | 13 +++++--- 5 files changed, 57 insertions(+), 48 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs index 8295795f00..d2c3df0872 100644 --- a/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Catch/Edit/Checks/CheckCatchAbnormalDifficultySettings.cs @@ -15,24 +15,25 @@ namespace osu.Game.Rulesets.Catch.Edit.Checks public override IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; + Issue? issue; - if (HasMoreThanOneDecimalPlace(diff.ApproachRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); + if (HasMoreThanOneDecimalPlace("Approach rate", diff.ApproachRate, out issue)) + yield return issue; - if (OutOfRange(diff.ApproachRate)) - yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); + if (OutOfRange("Approach rate", diff.ApproachRate, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.CircleSize)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); + if (HasMoreThanOneDecimalPlace("Circle size", diff.CircleSize, out issue)) + yield return issue; - if (OutOfRange(diff.CircleSize)) - yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); + if (OutOfRange("Circle size", diff.CircleSize, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.DrainRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + if (HasMoreThanOneDecimalPlace("Drain rate", diff.DrainRate, out issue)) + yield return issue; - if (OutOfRange(diff.DrainRate)) - yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + if (OutOfRange("Drain rate", diff.DrainRate, out issue)) + yield return issue; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs index ae0cc3aa4c..233c602c21 100644 --- a/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Mania/Edit/Checks/CheckManiaAbnormalDifficultySettings.cs @@ -15,18 +15,19 @@ namespace osu.Game.Rulesets.Mania.Edit.Checks public override IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; + Issue? issue; - if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + if (HasMoreThanOneDecimalPlace("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (OutOfRange(diff.OverallDifficulty)) - yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + if (OutOfRange("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.DrainRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + if (HasMoreThanOneDecimalPlace("Drain rate", diff.DrainRate, out issue)) + yield return issue; - if (OutOfRange(diff.DrainRate)) - yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + if (OutOfRange("Drain rate", diff.DrainRate, out issue)) + yield return issue; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs index 7ad861f317..1c44d54633 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOsuAbnormalDifficultySettings.cs @@ -15,30 +15,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public override IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; + Issue? issue; - if (HasMoreThanOneDecimalPlace(diff.ApproachRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Approach rate", diff.ApproachRate); + if (HasMoreThanOneDecimalPlace("Approach rate", diff.ApproachRate, out issue)) + yield return issue; - if (OutOfRange(diff.ApproachRate)) - yield return new IssueTemplateOutOfRange(this).Create("Approach rate", diff.ApproachRate); + if (OutOfRange("Approach rate", diff.ApproachRate, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + if (HasMoreThanOneDecimalPlace("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (OutOfRange(diff.OverallDifficulty)) - yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + if (OutOfRange("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.CircleSize)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Circle size", diff.CircleSize); + if (HasMoreThanOneDecimalPlace("Circle size", diff.CircleSize, out issue)) + yield return issue; - if (OutOfRange(diff.CircleSize)) - yield return new IssueTemplateOutOfRange(this).Create("Circle size", diff.CircleSize); + if (OutOfRange("Circle size", diff.CircleSize, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.DrainRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + if (HasMoreThanOneDecimalPlace("Drain rate", diff.DrainRate, out issue)) + yield return issue; - if (OutOfRange(diff.DrainRate)) - yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + if (OutOfRange("Drain rate", diff.DrainRate, out issue)) + yield return issue; } } } diff --git a/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs index 10e2867ca0..38ba7b1b01 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Checks/CheckTaikoAbnormalDifficultySettings.cs @@ -15,18 +15,19 @@ namespace osu.Game.Rulesets.Taiko.Edit.Checks public override IEnumerable Run(BeatmapVerifierContext context) { var diff = context.Beatmap.Difficulty; + Issue? issue; - if (HasMoreThanOneDecimalPlace(diff.OverallDifficulty)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Overall difficulty", diff.OverallDifficulty); + if (HasMoreThanOneDecimalPlace("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (OutOfRange(diff.OverallDifficulty)) - yield return new IssueTemplateOutOfRange(this).Create("Overall difficulty", diff.OverallDifficulty); + if (OutOfRange("Overall difficulty", diff.OverallDifficulty, out issue)) + yield return issue; - if (HasMoreThanOneDecimalPlace(diff.DrainRate)) - yield return new IssueTemplateMoreThanOneDecimal(this).Create("Drain rate", diff.DrainRate); + if (HasMoreThanOneDecimalPlace("Drain rate", diff.DrainRate, out issue)) + yield return issue; - if (OutOfRange(diff.DrainRate)) - yield return new IssueTemplateOutOfRange(this).Create("Drain rate", diff.DrainRate); + if (OutOfRange("Drain rate", diff.DrainRate, out issue)) + yield return issue; } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs index 93592a866b..638f0cfd53 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAbnormalDifficultySettings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks @@ -21,14 +22,18 @@ namespace osu.Game.Rulesets.Edit.Checks /// /// If the setting is out of the boundaries set by the editor (0 - 10) /// - protected bool OutOfRange(float setting) + protected bool OutOfRange(string setting, float value, [NotNullWhen(true)] out Issue? issue) { - return setting < 0f || setting > 10f; + bool hasIssue = value < 0f || value > 10f; + issue = hasIssue ? new IssueTemplateOutOfRange(this).Create(setting, value) : null; + return hasIssue; } - protected bool HasMoreThanOneDecimalPlace(float setting) + protected bool HasMoreThanOneDecimalPlace(string setting, float value, [NotNullWhen(true)] out Issue? issue) { - return float.Round(setting, 1) != setting; + bool hasIssue = float.Round(value, 1) != value; + issue = hasIssue ? new IssueTemplateMoreThanOneDecimal(this).Create(setting, value) : null; + return hasIssue; } public class IssueTemplateMoreThanOneDecimal : IssueTemplate From c24eb066dc4927580ffecfe79460819bc5486f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 12:03:24 +0100 Subject: [PATCH 122/581] Update tests to match new expected behaviour Co-authored-by: Vlad Frangu --- .../Filtering/FilterQueryParserTest.cs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index bd706b5b4c..ea14412f55 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -273,12 +273,21 @@ namespace osu.Game.Tests.NonVisual.Filtering } [Test] - public void TestApplyStatusMatches() + public void TestApplyMultipleEqualityStatusQueries() { const string query = "status=ranked status=loved"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Is.Empty); + } + + [Test] + public void TestApplyEqualStatusQueryWithMultipleValues() + { + const string query = "status=ranked,loved"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.That(filterCriteria.OnlineStatus.Values, Is.Not.Empty); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Loved)); } @@ -289,13 +298,43 @@ namespace osu.Game.Tests.NonVisual.Filtering const string query = "status>=r"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.IsNotEmpty(filterCriteria.OnlineStatus.Values); + Assert.That(filterCriteria.OnlineStatus.Values, Has.Count.EqualTo(4)); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Ranked)); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Approved)); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Qualified)); Assert.That(filterCriteria.OnlineStatus.Values, Contains.Item(BeatmapOnlineStatus.Loved)); } + [Test] + public void TestApplyRangeStatusWithMultipleMatchesQuery() + { + const string query = "status>=r,l"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.That(filterCriteria.OnlineStatus.Values, Is.EquivalentTo(Enum.GetValues())); + } + + [Test] + public void TestApplyTwoRangeStatusQuery() + { + const string query = "status>r status Date: Tue, 26 Mar 2024 12:20:38 +0100 Subject: [PATCH 123/581] Update `OptionalSet` implementation to intersect across multiple filters rather than union --- osu.Game/Screens/Select/FilterCriteria.cs | 9 +- osu.Game/Screens/Select/FilterQueryParser.cs | 86 ++++++++++++-------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 6750b07aba..01b0e9b7d9 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -114,17 +114,18 @@ namespace osu.Game.Screens.Select public IRulesetFilterCriteria? RulesetCriteria { get; set; } - public struct OptionalSet : IEquatable> - where T : struct + public readonly struct OptionalSet : IEquatable> + where T : struct, Enum { - public bool HasFilter => Values.Count > 0; + public bool HasFilter => true; public bool IsInRange(T value) => Values.Contains(value); - public HashSet Values = new HashSet(); + public HashSet Values { get; } public OptionalSet() { + Values = Enum.GetValues().ToHashSet(); } public bool Equals(OptionalSet other) => Values.SetEquals(other.Values); diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 32b533e18d..4e49495f47 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Select return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); case "status": - return TryUpdateCriteriaSet(ref criteria.OnlineStatus, op, value, tryParseEnum); + return TryUpdateCriteriaSet(ref criteria.OnlineStatus, op, value); case "creator": case "author": @@ -302,54 +302,70 @@ namespace osu.Game.Screens.Select /// /// Attempts to parse a keyword filter of type , - /// from the specified and . - /// If can be parsed into using , the function returns true + /// from the specified and . + /// If can be parsed successfully, the function returns true /// and the resulting range constraint is stored into the 's expected values. /// /// The to store the parsed data into, if successful. /// The operator for the keyword filter. - /// The value of the keyword filter. - /// Function used to determine if can be converted to type . - public static bool TryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, string val, TryParseFunction parseFunction) - where T : struct, Enum - => parseFunction.Invoke(val, out var converted) && tryUpdateCriteriaSet(ref range, op, converted); - - private static bool tryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, T pivotValue) + /// The value of the keyword filter. + public static bool TryUpdateCriteriaSet(ref FilterCriteria.OptionalSet range, Operator op, string filterValue) where T : struct, Enum { - var allDefinedValues = Enum.GetValues(); + var matchingValues = new HashSet(); - foreach (var val in allDefinedValues) + if (op == Operator.Equal && filterValue.Contains(',')) { - int compareResult = Comparer.Default.Compare(val, pivotValue); + string[] splitValues = filterValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - switch (op) + foreach (string splitValue in splitValues) { - case Operator.Less: - if (compareResult < 0) range.Values.Add(val); - break; - - case Operator.LessOrEqual: - if (compareResult <= 0) range.Values.Add(val); - break; - - case Operator.Equal: - if (compareResult == 0) range.Values.Add(val); - break; - - case Operator.GreaterOrEqual: - if (compareResult >= 0) range.Values.Add(val); - break; - - case Operator.Greater: - if (compareResult > 0) range.Values.Add(val); - break; - - default: + if (!tryParseEnum(splitValue, out var parsedValue)) return false; + + matchingValues.Add(parsedValue); + } + } + else + { + if (!tryParseEnum(filterValue, out var pivotValue)) + return false; + + var allDefinedValues = Enum.GetValues(); + + foreach (var val in allDefinedValues) + { + int compareResult = Comparer.Default.Compare(val, pivotValue); + + switch (op) + { + case Operator.Less: + if (compareResult < 0) matchingValues.Add(val); + break; + + case Operator.LessOrEqual: + if (compareResult <= 0) matchingValues.Add(val); + break; + + case Operator.Equal: + if (compareResult == 0) matchingValues.Add(val); + break; + + case Operator.GreaterOrEqual: + if (compareResult >= 0) matchingValues.Add(val); + break; + + case Operator.Greater: + if (compareResult > 0) matchingValues.Add(val); + break; + + default: + return false; + } } } + range.Values.IntersectWith(matchingValues); return true; } From 9474156df4bb33bda65c0bd2d68d565825f1b3d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 20:21:12 +0800 Subject: [PATCH 124/581] Improve equality implementations --- .../API/Requests/Responses/APIMenuContent.cs | 10 +--------- .../API/Requests/Responses/APIMenuImage.cs | 17 +++++------------ 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs index 7b53488030..6aad0f6c87 100644 --- a/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs +++ b/osu.Game/Online/API/Requests/Responses/APIMenuContent.cs @@ -23,15 +23,7 @@ namespace osu.Game.Online.API.Requests.Responses return Images.SequenceEqual(other.Images); } - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - - if (obj.GetType() != GetType()) return false; - - return Equals((APIMenuContent)obj); - } + public override bool Equals(object? other) => other is APIMenuContent content && Equals(content); public override int GetHashCode() { diff --git a/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs index 42129ca96e..8aff08099a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs +++ b/osu.Game/Online/API/Requests/Responses/APIMenuImage.cs @@ -28,34 +28,27 @@ namespace osu.Game.Online.API.Requests.Responses /// The time at which this item should begin displaying. If null, will display immediately. /// [JsonProperty(@"begins")] - public DateTimeOffset? Begins { get; set; } + public DateTimeOffset? Begins { get; init; } /// /// The time at which this item should stop displaying. If null, will display indefinitely. /// [JsonProperty(@"expires")] - public DateTimeOffset? Expires { get; set; } + public DateTimeOffset? Expires { get; init; } public bool Equals(APIMenuImage? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Image == other.Image && Url == other.Url; + return Image == other.Image && Url == other.Url && Begins == other.Begins && Expires == other.Expires; } - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - - return Equals((APIMenuImage)obj); - } + public override bool Equals(object? other) => other is APIMenuImage content && Equals(content); public override int GetHashCode() { - return HashCode.Combine(Image, Url); + return HashCode.Combine(Image, Url, Begins, Expires); } } } From fd649edabae36603cccfc8a847a81de05b32f257 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 20:21:48 +0800 Subject: [PATCH 125/581] Also don't rotate images during a drag operation --- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 37bec7aa63..260c021719 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -120,8 +120,8 @@ namespace osu.Game.Screens.Menu { nextDisplay?.Cancel(); - // If the user is hovering a banner, don't rotate yet. - bool anyHovered = content.Any(i => i.IsHovered); + // If the user is interacting with a banner, don't rotate yet. + bool anyHovered = content.Any(i => i.IsHovered || i.IsDragged); if (!anyHovered) { @@ -242,6 +242,8 @@ namespace osu.Game.Screens.Menu .ScaleTo(1, 500, Easing.OutElastic); base.OnMouseUp(e); } + + protected override bool OnDragStart(DragStartEvent e) => true; } } } From e77d4c8cfaf66e1e717b0094632a3704b77e73f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 20:28:03 +0800 Subject: [PATCH 126/581] Remove unnecessary `Math.Max` --- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 260c021719..6f98b73939 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -130,8 +130,8 @@ namespace osu.Game.Screens.Menu // To handle expiration simply, arrange all images in best-next order. // Fade in the first valid one, then handle fading out the last if required. var currentRotation = content - .Skip(Math.Max(0, previousIndex) + 1) - .Concat(content.Take(Math.Max(0, previousIndex) + 1)); + .Skip(previousIndex + 1) + .Concat(content.Take(previousIndex + 1)); // After the loop, displayIndex will be the new valid index or -1 if // none valid. From dee88573a756f9652628d13f2033cd4b78244870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 13:44:12 +0100 Subject: [PATCH 127/581] Fix test failure in visual browser I'm not sure why it's failing headless and I'm not particularly interested in finding that out right now. --- osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index 380085ce04..60e42838d8 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Menu; +using osuTK; namespace osu.Game.Tests.Visual.Menus { @@ -95,7 +96,7 @@ namespace osu.Game.Tests.Visual.Menus } }); - AddUntilStep("wait for no image shown", () => !onlineMenuBanner.ChildrenOfType().Any()); + AddUntilStep("wait for no image shown", () => onlineMenuBanner.ChildrenOfType().Single().Size, () => Is.EqualTo(Vector2.Zero)); AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); From b4ccbc68e447f0dba68470d70510195bfad009f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 21:20:22 +0800 Subject: [PATCH 128/581] Fix failing test --- osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs index 60e42838d8..0b90fd13c3 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneOnlineMenuBanner.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Menus } }); - AddUntilStep("wait for no image shown", () => onlineMenuBanner.ChildrenOfType().Single().Size, () => Is.EqualTo(Vector2.Zero)); + AddUntilStep("wait for no image shown", () => onlineMenuBanner.ChildrenOfType().SingleOrDefault()?.Size, () => Is.EqualTo(Vector2.Zero)); AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent()); From a5f15a119e325f954e8f5358227269a99dec4e5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 22:51:54 +0800 Subject: [PATCH 129/581] Apply rate adjust fix in all cases rather than specifically for `Clock.Rate == 1` --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index a14dfd4d64..de9a5aff3a 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -96,10 +96,8 @@ namespace osu.Game.Graphics.Containers // In the case of gameplay, we are usually within a hierarchy with the correct rate applied to our `Drawable.Clock`. // This means that the amount of early adjustment is adjusted in line with audio track rate changes. // But other cases like the osu! logo at the main menu won't correctly have this rate information. - // - // So for cases where the rate of the source isn't in sync with our hierarchy, let's assume we need to account for it locally. - if (Clock.Rate == 1 && BeatSyncSource.Clock.Rate != Clock.Rate) - early *= BeatSyncSource.Clock.Rate; + // We can adjust here to ensure the applied early activation always matches expectations. + early *= BeatSyncSource.Clock.Rate / Clock.Rate; currentTrackTime = BeatSyncSource.Clock.CurrentTime + early; From 4c1a1b54be7ffab08eb6e2713dbeca953f15044c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 27 Mar 2024 00:07:51 +0900 Subject: [PATCH 130/581] Fix NVAPI startup exception on non-Windows platforms --- osu.Desktop/NVAPI.cs | 2 ++ osu.Desktop/Program.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index 78a814c585..554f89a847 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -8,11 +8,13 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using osu.Framework.Logging; namespace osu.Desktop { [SuppressMessage("ReSharper", "InconsistentNaming")] + [SupportedOSPlatform("windows")] internal static class NVAPI { private const string osu_filename = "osu!.exe"; diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 73670adc49..2d7ec5aa5f 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -70,7 +70,8 @@ namespace osu.Desktop // NVIDIA profiles are based on the executable name of a process. // Lazer and stable share the same executable name. // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. - NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; + if (OperatingSystem.IsWindows()) + NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; // Back up the cwd before DesktopGameHost changes it string cwd = Environment.CurrentDirectory; From 01a72d5afaa788e707308bea0764565ad1b044e6 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 26 Mar 2024 12:10:40 -0300 Subject: [PATCH 131/581] Fix combo not reversing properly depending on the order of selection --- .../Edit/CatchSelectionHandler.cs | 17 +++++++++-------- .../Edit/OsuSelectionHandler.cs | 17 +++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index e3d2347c4d..f8fe9805e6 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -76,17 +76,15 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleReverse() { - var hitObjects = EditorBeatmap.SelectedHitObjects; + var hitObjects = EditorBeatmap.SelectedHitObjects + .OfType() + .OrderBy(obj => obj.StartTime) + .ToList(); double selectionStartTime = SelectedItems.Min(h => h.StartTime); double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); - var newComboOrder = hitObjects - .OfType() - .Select(obj => obj.NewCombo) - .ToList(); - - newComboOrder.Reverse(); + var newComboOrder = hitObjects.Select(obj => obj.NewCombo).ToList(); foreach (var h in hitObjects) { @@ -101,10 +99,13 @@ namespace osu.Game.Rulesets.Catch.Edit } } + // re-order objects again after flipping their times + hitObjects = [.. hitObjects.OrderBy(obj => obj.StartTime)]; + int i = 0; foreach (bool newCombo in newComboOrder) { - hitObjects.OfType().ToList()[i].NewCombo = newCombo; + hitObjects[i].NewCombo = newCombo; i++; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index df39ab8fce..0e889cab81 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -78,19 +78,17 @@ namespace osu.Game.Rulesets.Osu.Edit public override bool HandleReverse() { - var hitObjects = EditorBeatmap.SelectedHitObjects; + var hitObjects = EditorBeatmap.SelectedHitObjects + .OfType() + .OrderBy(obj => obj.StartTime) + .ToList(); double endTime = hitObjects.Max(h => h.GetEndTime()); double startTime = hitObjects.Min(h => h.StartTime); bool moreThanOneObject = hitObjects.Count > 1; - var newComboOrder = hitObjects - .OfType() - .Select(obj => obj.NewCombo) - .ToList(); - - newComboOrder.Reverse(); + var newComboOrder = hitObjects.Select(obj => obj.NewCombo).ToList(); foreach (var h in hitObjects) { @@ -104,10 +102,13 @@ namespace osu.Game.Rulesets.Osu.Edit } } + // re-order objects again after flipping their times + hitObjects = [.. hitObjects.OrderBy(obj => obj.StartTime)]; + int i = 0; foreach (bool newCombo in newComboOrder) { - hitObjects.OfType().ToList()[i].NewCombo = newCombo; + hitObjects[i].NewCombo = newCombo; i++; } From e02ad6cf94d3a9bba0cbcac6ecee5a0125f9da89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Mar 2024 23:38:34 +0800 Subject: [PATCH 132/581] Fix test regression --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index fdb1cac3e5..8c81431770 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private SessionStatics statics { get; set; } = null!; private ScoreAccessibleSoloPlayer currentPlayer = null!; - private readonly ManualClock manualClock = new ManualClock { Rate = 0 }; + private readonly ManualClock manualClock = new ManualClock { Rate = 1 }; protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) => new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(manualClock), Audio); From 0b29a762b8aa712d1a0b697f367ae809a0896f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Mar 2024 17:36:00 +0100 Subject: [PATCH 133/581] Add precautionary guard to avoid potential div-by-zero Probably wouldn't happen outside of tests, but I'd rather not find out next release. --- osu.Game/Graphics/Containers/BeatSyncedContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index de9a5aff3a..7210371ebf 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -97,7 +97,8 @@ namespace osu.Game.Graphics.Containers // This means that the amount of early adjustment is adjusted in line with audio track rate changes. // But other cases like the osu! logo at the main menu won't correctly have this rate information. // We can adjust here to ensure the applied early activation always matches expectations. - early *= BeatSyncSource.Clock.Rate / Clock.Rate; + if (Clock.Rate > 0) + early *= BeatSyncSource.Clock.Rate / Clock.Rate; currentTrackTime = BeatSyncSource.Clock.CurrentTime + early; From 600098d845611a45147154690cc09f992c961119 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 27 Mar 2024 04:05:04 +0900 Subject: [PATCH 134/581] Fix bulbs on Catmull sliders --- osu.Game/Rulesets/Objects/SliderPath.cs | 68 +++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index f33a07f082..5398d6c45f 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -42,6 +42,17 @@ namespace osu.Game.Rulesets.Objects private readonly List cumulativeLength = new List(); private readonly Cached pathCache = new Cached(); + /// + /// Any additional length of the path which was optimised out during piecewise approximation, but should still be considered as part of . + /// + /// + /// This is a hack for Catmull paths. + /// + private double optimisedLength; + + /// + /// The final calculated length of the path. + /// private double calculatedLength; private readonly List segmentEnds = new List(); @@ -244,6 +255,7 @@ namespace osu.Game.Rulesets.Objects { calculatedPath.Clear(); segmentEnds.Clear(); + optimisedLength = 0; if (ControlPoints.Count == 0) return; @@ -268,7 +280,8 @@ namespace osu.Game.Rulesets.Objects calculatedPath.Add(segmentVertices[0]); else if (segmentVertices.Length > 1) { - List subPath = calculateSubPath(segmentVertices, segmentType); + List subPath = calculateSubPath(segmentVertices, segmentType, ref optimisedLength); + // Skip the first vertex if it is the same as the last vertex from the previous segment bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0]; @@ -287,7 +300,7 @@ namespace osu.Game.Rulesets.Objects } } - private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) + private static List calculateSubPath(ReadOnlySpan subControlPoints, PathType type, ref double optimisedLength) { switch (type.Type) { @@ -295,6 +308,7 @@ namespace osu.Game.Rulesets.Objects return PathApproximator.LinearToPiecewiseLinear(subControlPoints); case SplineType.PerfectCurve: + { if (subControlPoints.Length != 3) break; @@ -305,9 +319,55 @@ namespace osu.Game.Rulesets.Objects break; return subPath; + } case SplineType.Catmull: - return PathApproximator.CatmullToPiecewiseLinear(subControlPoints); + { + List subPath = PathApproximator.CatmullToPiecewiseLinear(subControlPoints); + + // At draw time, osu!stable optimises paths by only keeping piecewise segments that are 6px apart. + // For the most part we don't care about this optimisation, and its additional heuristics are hard to reproduce in every implementation. + // + // However, it matters for Catmull paths which form "bulbs" around sequential knots with identical positions, + // so we'll apply a very basic form of the optimisation here and return a length representing the optimised portion. + // The returned length is important so that the optimisation doesn't cause the path to get extended to match the value of ExpectedDistance. + + List optimisedPath = new List(subPath.Count); + + Vector2? lastStart = null; + double lengthRemovedSinceStart = 0; + + for (int i = 0; i < subPath.Count; i++) + { + if (lastStart == null) + { + optimisedPath.Add(subPath[i]); + lastStart = subPath[i]; + continue; + } + + Debug.Assert(i > 0); + + double distFromStart = Vector2.Distance(lastStart.Value, subPath[i]); + lengthRemovedSinceStart += Vector2.Distance(subPath[i - 1], subPath[i]); + + // See PathApproximator.catmull_detail. + const int catmull_detail = 50; + const int catmull_segment_length = catmull_detail * 2; + + // Either 6px from the start, the last vertex at every knot, or the end of the path. + if (distFromStart > 6 || (i + 1) % catmull_segment_length == 0 || i == subPath.Count - 1) + { + optimisedPath.Add(subPath[i]); + optimisedLength += lengthRemovedSinceStart - distFromStart; + + lastStart = null; + lengthRemovedSinceStart = 0; + } + } + + return optimisedPath; + } } return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length); @@ -315,7 +375,7 @@ namespace osu.Game.Rulesets.Objects private void calculateLength() { - calculatedLength = 0; + calculatedLength = optimisedLength; cumulativeLength.Clear(); cumulativeLength.Add(0); From 4490dbf8960214dcc9d3f67dbfc88ab5255c6e94 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 27 Mar 2024 13:37:47 +0900 Subject: [PATCH 135/581] Update diffcalc workflow --- .github/workflows/diffcalc.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 7a2dcecb9c..2ed176fe8d 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -110,10 +110,14 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.comment.body, '!diffcalc') }} steps: - name: Check permissions - if: ${{ github.event_name != 'workflow_dispatch' }} - uses: actions-cool/check-user-permission@a0668c9aec87f3875fc56170b6452a453e9dd819 # v2.2.0 - with: - require: 'write' + run: | + ALLOWED_USERS=(smoogipoo peppy bdach) + for i in "${ALLOWED_USERS[@]}"; do + if [[ "${{ github.actor }}" == "$i" ]]; then + exit 0 + fi + done + exit 1 create-comment: name: Create PR comment From 60c93d2c6de6690913ba6f9552615116c2470a29 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 27 Mar 2024 08:22:51 -0300 Subject: [PATCH 136/581] Add reverse pattern visual tests --- .../Editor/TestSceneCatchReverseSelection.cs | 313 ++++++++++++++++ .../Editor/TestSceneOsuReverseSelection.cs | 347 ++++++++++++++++++ 2 files changed, 660 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs new file mode 100644 index 0000000000..c8a48f76eb --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs @@ -0,0 +1,313 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + [TestFixture] + public partial class TestSceneCatchReverseSelection : TestSceneEditor + { + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + [Test] + public void TestReverseSelectionTwoFruits() + { + float fruit1OldX = default; + float fruit2OldX = default; + + addObjects([ + new Fruit + { + StartTime = 200, + X = fruit1OldX = 0, + }, + new Fruit + { + StartTime = 400, + X = fruit2OldX = 20, + } + ]); + + selectEverything(); + reverseSelection(); + + AddAssert("fruit1 is at fruit2's X", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, + () => Is.EqualTo(fruit2OldX) + ); + + AddAssert("fruit2 is at fruit1's X", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, + () => Is.EqualTo(fruit1OldX) + ); + + AddAssert("fruit2 is not a new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionThreeFruits() + { + float fruit1OldX = default; + float fruit2OldX = default; + float fruit3OldX = default; + + addObjects([ + new Fruit + { + StartTime = 200, + X = fruit1OldX = 0, + }, + new Fruit + { + StartTime = 400, + X = fruit2OldX = 20, + }, + new Fruit + { + StartTime = 600, + X = fruit3OldX = 40, + } + ]); + + selectEverything(); + reverseSelection(); + + AddAssert("fruit1 is at fruit3's X", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, + () => Is.EqualTo(fruit3OldX) + ); + + AddAssert("fruit2's X is unchanged", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, + () => Is.EqualTo(fruit2OldX) + ); + + AddAssert("fruit3's is at fruit1's X", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).EffectiveX, + () => Is.EqualTo(fruit1OldX) + ); + + AddAssert("fruit3 is not a new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionFruitAndJuiceStream() + { + addObjects([ + new Fruit + { + StartTime = 200, + X = 0, + }, + new JuiceStream + { + StartTime = 400, + X = 20, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(50)) + } + } + } + ]); + + selectEverything(); + reverseSelection(); + + AddAssert("First element is juice stream", + () => EditorBeatmap.HitObjects.First().GetType(), + () => Is.EqualTo(typeof(JuiceStream)) + ); + + AddAssert("Last element is fruit", + () => EditorBeatmap.HitObjects.Last().GetType(), + () => Is.EqualTo(typeof(Fruit)) + ); + + AddAssert("Fruit is not new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionTwoFruitsAndJuiceStream() + { + addObjects([ + new Fruit + { + StartTime = 200, + X = 0, + }, + new Fruit + { + StartTime = 400, + X = 20, + }, + new JuiceStream + { + StartTime = 600, + X = 40, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(50)) + } + } + } + ]); + + selectEverything(); + reverseSelection(); + + AddAssert("First element is juice stream", + () => EditorBeatmap.HitObjects.First().GetType(), + () => Is.EqualTo(typeof(JuiceStream)) + ); + + AddAssert("Middle element is Fruit", + () => EditorBeatmap.HitObjects.ElementAt(1).GetType(), + () => Is.EqualTo(typeof(Fruit)) + ); + + AddAssert("Last element is Fruit", + () => EditorBeatmap.HitObjects.Last().GetType(), + () => Is.EqualTo(typeof(Fruit)) + ); + + AddAssert("Last fruit is not new combo", + () => EditorBeatmap.HitObjects.OfType().Last().NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionTwoCombos() + { + float fruit1OldX = default; + float fruit2OldX = default; + float fruit3OldX = default; + + float fruit4OldX = default; + float fruit5OldX = default; + float fruit6OldX = default; + + addObjects([ + new Fruit + { + StartTime = 200, + X = fruit1OldX = 0, + }, + new Fruit + { + StartTime = 400, + X = fruit2OldX = 20, + }, + new Fruit + { + StartTime = 600, + X = fruit3OldX = 40, + }, + + new Fruit + { + StartTime = 800, + NewCombo = true, + X = fruit4OldX = 60, + }, + new Fruit + { + StartTime = 1000, + X = fruit5OldX = 80, + }, + new Fruit + { + StartTime = 1200, + X = fruit6OldX = 100, + } + ]); + + selectEverything(); + reverseSelection(); + + AddAssert("fruit1 is at fruit6 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, + () => Is.EqualTo(fruit6OldX) + ); + + AddAssert("fruit2 is at fruit5 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, + () => Is.EqualTo(fruit5OldX) + ); + + AddAssert("fruit3 is at fruit4 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).EffectiveX, + () => Is.EqualTo(fruit4OldX) + ); + + AddAssert("fruit4 is at fruit3 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(3).EffectiveX, + () => Is.EqualTo(fruit3OldX) + ); + + AddAssert("fruit5 is at fruit2 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(4).EffectiveX, + () => Is.EqualTo(fruit2OldX) + ); + + AddAssert("fruit6 is at fruit1 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(5).EffectiveX, + () => Is.EqualTo(fruit1OldX) + ); + + AddAssert("fruit1 is new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).NewCombo, + () => Is.EqualTo(true) + ); + + AddAssert("fruit4 is new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(3).NewCombo, + () => Is.EqualTo(true) + ); + } + + private void addObjects(CatchHitObject[] hitObjects) => AddStep("Add objects", () => EditorBeatmap.AddRange(hitObjects)); + + private void selectEverything() + { + AddStep("Select everything", () => + { + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + } + + private void reverseSelection() + { + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs new file mode 100644 index 0000000000..33104288ab --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs @@ -0,0 +1,347 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + [TestFixture] + public partial class TestSceneOsuReverseSelection : TestSceneOsuEditor + { + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + [Test] + public void TestReverseSelectionTwoCircles() + { + Vector2 circle1OldPosition = default; + Vector2 circle2OldPosition = default; + + AddStep("Add circles", () => + { + var circle1 = new HitCircle + { + StartTime = 0, + Position = circle1OldPosition = new Vector2(208, 240) + }; + var circle2 = new HitCircle + { + StartTime = 200, + Position = circle2OldPosition = new Vector2(256, 144) + }; + + EditorBeatmap.AddRange([circle1, circle2]); + }); + + AddStep("Select circles", () => + { + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + + AddAssert("circle1 is at circle2 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, + () => Is.EqualTo(circle2OldPosition) + ); + + AddAssert("circle2 is at circle1 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, + () => Is.EqualTo(circle1OldPosition) + ); + + AddAssert("circle2 is not a new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionThreeCircles() + { + Vector2 circle1OldPosition = default; + Vector2 circle2OldPosition = default; + Vector2 circle3OldPosition = default; + + AddStep("Add circles", () => + { + var circle1 = new HitCircle + { + StartTime = 0, + Position = circle1OldPosition = new Vector2(208, 240) + }; + var circle2 = new HitCircle + { + StartTime = 200, + Position = circle2OldPosition = new Vector2(256, 144) + }; + var circle3 = new HitCircle + { + StartTime = 400, + Position = circle3OldPosition = new Vector2(304, 240) + }; + + EditorBeatmap.AddRange([circle1, circle2, circle3]); + }); + + AddStep("Select circles", () => + { + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + + AddAssert("circle1 is at circle3 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, + () => Is.EqualTo(circle3OldPosition) + ); + + AddAssert("circle3 is at circle1 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).Position, + () => Is.EqualTo(circle1OldPosition) + ); + + AddAssert("circle3 is not a new combo", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).NewCombo, + () => Is.EqualTo(false) + ); + } + + [Test] + public void TestReverseSelectionCircleAndSlider() + { + Vector2 circleOldPosition = default; + Vector2 sliderHeadOldPosition = default; + Vector2 sliderTailOldPosition = default; + + AddStep("Add objects", () => + { + var circle = new HitCircle + { + StartTime = 0, + Position = circleOldPosition = new Vector2(208, 240) + }; + var slider = new Slider + { + StartTime = 200, + Position = sliderHeadOldPosition = new Vector2(257, 144), + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + } + } + }; + + sliderTailOldPosition = slider.EndPosition; + + EditorBeatmap.AddRange([circle, slider]); + }); + + AddStep("Select objects", () => + { + var circle = (HitCircle)EditorBeatmap.HitObjects[0]; + var slider = (Slider)EditorBeatmap.HitObjects[1]; + + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + + AddAssert("circle is at the same position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, + () => Is.EqualTo(circleOldPosition) + ); + + AddAssert("Slider head is at slider tail", () => + Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, sliderTailOldPosition) < 1); + + AddAssert("Slider tail is at slider head", () => + Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).EndPosition, sliderHeadOldPosition) < 1); + } + + [Test] + public void TestReverseSelectionTwoCirclesAndSlider() + { + Vector2 circle1OldPosition = default; + Vector2 circle2OldPosition = default; + + Vector2 sliderHeadOldPosition = default; + Vector2 sliderTailOldPosition = default; + + AddStep("Add objects", () => + { + var circle1 = new HitCircle + { + StartTime = 0, + Position = circle1OldPosition = new Vector2(208, 240) + }; + var circle2 = new HitCircle + { + StartTime = 200, + Position = circle2OldPosition = new Vector2(256, 144) + }; + var slider = new Slider + { + StartTime = 200, + Position = sliderHeadOldPosition = new Vector2(304, 240), + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + } + } + }; + + sliderTailOldPosition = slider.EndPosition; + + EditorBeatmap.AddRange([circle1, circle2, slider]); + }); + + AddStep("Select objects", () => + { + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + + AddAssert("circle1 is at circle2 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, + () => Is.EqualTo(circle2OldPosition) + ); + + AddAssert("circle2 is at circle1 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, + () => Is.EqualTo(circle1OldPosition) + ); + + AddAssert("Slider head is at slider tail", () => + Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, sliderTailOldPosition) < 1); + + AddAssert("Slider tail is at slider head", () => + Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).EndPosition, sliderHeadOldPosition) < 1); + } + + [Test] + public void TestReverseSelectionTwoCombos() + { + Vector2 circle1OldPosition = default; + Vector2 circle2OldPosition = default; + Vector2 circle3OldPosition = default; + + Vector2 circle4OldPosition = default; + Vector2 circle5OldPosition = default; + Vector2 circle6OldPosition = default; + + AddStep("Add circles", () => + { + var circle1 = new HitCircle + { + StartTime = 0, + Position = circle1OldPosition = new Vector2(216, 240) + }; + var circle2 = new HitCircle + { + StartTime = 200, + Position = circle2OldPosition = new Vector2(120, 192) + }; + var circle3 = new HitCircle + { + StartTime = 400, + Position = circle3OldPosition = new Vector2(216, 144) + }; + + var circle4 = new HitCircle + { + StartTime = 646, + NewCombo = true, + Position = circle4OldPosition = new Vector2(296, 240) + }; + var circle5 = new HitCircle + { + StartTime = 846, + Position = circle5OldPosition = new Vector2(392, 162) + }; + var circle6 = new HitCircle + { + StartTime = 1046, + Position = circle6OldPosition = new Vector2(296, 144) + }; + + EditorBeatmap.AddRange([circle1, circle2, circle3, circle4, circle5, circle6]); + }); + + AddStep("Select circles", () => + { + EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + }); + + AddStep("Reverse selection", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.G); + InputManager.ReleaseKey(Key.LControl); + }); + + AddAssert("circle1 is at circle6 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, + () => Is.EqualTo(circle6OldPosition) + ); + + AddAssert("circle2 is at circle5 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, + () => Is.EqualTo(circle5OldPosition) + ); + + AddAssert("circle3 is at circle4 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(2).Position, + () => Is.EqualTo(circle4OldPosition) + ); + + AddAssert("circle4 is at circle3 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(3).Position, + () => Is.EqualTo(circle3OldPosition) + ); + + AddAssert("circle5 is at circle2 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(4).Position, + () => Is.EqualTo(circle2OldPosition) + ); + + AddAssert("circle6 is at circle1 position", + () => EditorBeatmap.HitObjects.OfType().ElementAt(5).Position, + () => Is.EqualTo(circle1OldPosition) + ); + } + } +} From 53900d5472ac14a41003f5353bd704e42e245a7b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 27 Mar 2024 18:56:26 +0100 Subject: [PATCH 137/581] Fix tests failing locally due to not using invariant culture --- osu.Game/Skinning/SkinImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinImporter.cs b/osu.Game/Skinning/SkinImporter.cs index 3e948a8afb..59c7f0ba26 100644 --- a/osu.Game/Skinning/SkinImporter.cs +++ b/osu.Game/Skinning/SkinImporter.cs @@ -132,7 +132,7 @@ namespace osu.Game.Skinning { // skins without a skin.ini are supposed to import using the "latest version" spec. // see https://github.com/peppy/osu-stable-reference/blob/1531237b63392e82c003c712faa028406073aa8f/osu!/Graphics/Skinning/SkinManager.cs#L297-L298 - newLines.Add($"Version: {SkinConfiguration.LATEST_VERSION}"); + newLines.Add(FormattableString.Invariant($"Version: {SkinConfiguration.LATEST_VERSION}")); // In the case a skin doesn't have a skin.ini yet, let's create one. writeNewSkinIni(); From a9cbabf71135ff97c587a1c01079b56850ef27f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Mar 2024 10:05:26 +0100 Subject: [PATCH 138/581] Simplify tests --- .../Editor/TestSceneCatchReverseSelection.cs | 196 ++++++------------ .../Editor/TestSceneOsuReverseSelection.cs | 177 ++++++---------- 2 files changed, 133 insertions(+), 240 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs index c8a48f76eb..36a0e3388e 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchReverseSelection.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 System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; @@ -20,93 +21,78 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestReverseSelectionTwoFruits() { - float fruit1OldX = default; - float fruit2OldX = default; + CatchHitObject[] objects = null!; + bool[] newCombos = null!; addObjects([ new Fruit { StartTime = 200, - X = fruit1OldX = 0, + X = 0, }, new Fruit { StartTime = 400, - X = fruit2OldX = 20, + X = 20, } ]); + AddStep("store objects & new combo data", () => + { + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); + }); + selectEverything(); reverseSelection(); - AddAssert("fruit1 is at fruit2's X", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, - () => Is.EqualTo(fruit2OldX) - ); - - AddAssert("fruit2 is at fruit1's X", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, - () => Is.EqualTo(fruit1OldX) - ); - - AddAssert("fruit2 is not a new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionThreeFruits() { - float fruit1OldX = default; - float fruit2OldX = default; - float fruit3OldX = default; + CatchHitObject[] objects = null!; + bool[] newCombos = null!; addObjects([ new Fruit { StartTime = 200, - X = fruit1OldX = 0, + X = 0, }, new Fruit { StartTime = 400, - X = fruit2OldX = 20, + X = 20, }, new Fruit { StartTime = 600, - X = fruit3OldX = 40, + X = 40, } ]); + AddStep("store objects & new combo data", () => + { + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); + }); + selectEverything(); reverseSelection(); - AddAssert("fruit1 is at fruit3's X", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, - () => Is.EqualTo(fruit3OldX) - ); - - AddAssert("fruit2's X is unchanged", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, - () => Is.EqualTo(fruit2OldX) - ); - - AddAssert("fruit3's is at fruit1's X", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).EffectiveX, - () => Is.EqualTo(fruit1OldX) - ); - - AddAssert("fruit3 is not a new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionFruitAndJuiceStream() { + CatchHitObject[] objects = null!; + bool[] newCombos = null!; + addObjects([ new Fruit { @@ -128,28 +114,25 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } ]); + AddStep("store objects & new combo data", () => + { + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); + }); + selectEverything(); reverseSelection(); - AddAssert("First element is juice stream", - () => EditorBeatmap.HitObjects.First().GetType(), - () => Is.EqualTo(typeof(JuiceStream)) - ); - - AddAssert("Last element is fruit", - () => EditorBeatmap.HitObjects.Last().GetType(), - () => Is.EqualTo(typeof(Fruit)) - ); - - AddAssert("Fruit is not new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionTwoFruitsAndJuiceStream() { + CatchHitObject[] objects = null!; + bool[] newCombos = null!; + addObjects([ new Fruit { @@ -176,122 +159,79 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor } ]); + AddStep("store objects & new combo data", () => + { + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); + }); + selectEverything(); reverseSelection(); - AddAssert("First element is juice stream", - () => EditorBeatmap.HitObjects.First().GetType(), - () => Is.EqualTo(typeof(JuiceStream)) - ); - - AddAssert("Middle element is Fruit", - () => EditorBeatmap.HitObjects.ElementAt(1).GetType(), - () => Is.EqualTo(typeof(Fruit)) - ); - - AddAssert("Last element is Fruit", - () => EditorBeatmap.HitObjects.Last().GetType(), - () => Is.EqualTo(typeof(Fruit)) - ); - - AddAssert("Last fruit is not new combo", - () => EditorBeatmap.HitObjects.OfType().Last().NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionTwoCombos() { - float fruit1OldX = default; - float fruit2OldX = default; - float fruit3OldX = default; - - float fruit4OldX = default; - float fruit5OldX = default; - float fruit6OldX = default; + CatchHitObject[] objects = null!; + bool[] newCombos = null!; addObjects([ new Fruit { StartTime = 200, - X = fruit1OldX = 0, + X = 0, }, new Fruit { StartTime = 400, - X = fruit2OldX = 20, + X = 20, }, new Fruit { StartTime = 600, - X = fruit3OldX = 40, + X = 40, }, new Fruit { StartTime = 800, NewCombo = true, - X = fruit4OldX = 60, + X = 60, }, new Fruit { StartTime = 1000, - X = fruit5OldX = 80, + X = 80, }, new Fruit { StartTime = 1200, - X = fruit6OldX = 100, + X = 100, } ]); + AddStep("store objects & new combo data", () => + { + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); + }); + selectEverything(); reverseSelection(); - AddAssert("fruit1 is at fruit6 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).EffectiveX, - () => Is.EqualTo(fruit6OldX) - ); - - AddAssert("fruit2 is at fruit5 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).EffectiveX, - () => Is.EqualTo(fruit5OldX) - ); - - AddAssert("fruit3 is at fruit4 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).EffectiveX, - () => Is.EqualTo(fruit4OldX) - ); - - AddAssert("fruit4 is at fruit3 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(3).EffectiveX, - () => Is.EqualTo(fruit3OldX) - ); - - AddAssert("fruit5 is at fruit2 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(4).EffectiveX, - () => Is.EqualTo(fruit2OldX) - ); - - AddAssert("fruit6 is at fruit1 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(5).EffectiveX, - () => Is.EqualTo(fruit1OldX) - ); - - AddAssert("fruit1 is new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).NewCombo, - () => Is.EqualTo(true) - ); - - AddAssert("fruit4 is new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(3).NewCombo, - () => Is.EqualTo(true) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } private void addObjects(CatchHitObject[] hitObjects) => AddStep("Add objects", () => EditorBeatmap.AddRange(hitObjects)); + private IEnumerable getObjects() => EditorBeatmap.HitObjects.OfType(); + + private IEnumerable getObjectNewCombos() => getObjects().Select(ho => ho.NewCombo); + private void selectEverything() { AddStep("Select everything", () => diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs index 33104288ab..28c1577fcb 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuReverseSelection.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 System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; @@ -20,30 +21,33 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestReverseSelectionTwoCircles() { - Vector2 circle1OldPosition = default; - Vector2 circle2OldPosition = default; + OsuHitObject[] objects = null!; + bool[] newCombos = null!; AddStep("Add circles", () => { var circle1 = new HitCircle { StartTime = 0, - Position = circle1OldPosition = new Vector2(208, 240) + Position = new Vector2(208, 240) }; var circle2 = new HitCircle { StartTime = 200, - Position = circle2OldPosition = new Vector2(256, 144) + Position = new Vector2(256, 144) }; EditorBeatmap.AddRange([circle1, circle2]); }); - AddStep("Select circles", () => + AddStep("store objects & new combo data", () => { - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); }); + AddStep("Select circles", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + AddStep("Reverse selection", () => { InputManager.PressKey(Key.LControl); @@ -51,55 +55,45 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.ReleaseKey(Key.LControl); }); - AddAssert("circle1 is at circle2 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, - () => Is.EqualTo(circle2OldPosition) - ); - - AddAssert("circle2 is at circle1 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, - () => Is.EqualTo(circle1OldPosition) - ); - - AddAssert("circle2 is not a new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionThreeCircles() { - Vector2 circle1OldPosition = default; - Vector2 circle2OldPosition = default; - Vector2 circle3OldPosition = default; + OsuHitObject[] objects = null!; + bool[] newCombos = null!; AddStep("Add circles", () => { var circle1 = new HitCircle { StartTime = 0, - Position = circle1OldPosition = new Vector2(208, 240) + Position = new Vector2(208, 240) }; var circle2 = new HitCircle { StartTime = 200, - Position = circle2OldPosition = new Vector2(256, 144) + Position = new Vector2(256, 144) }; var circle3 = new HitCircle { StartTime = 400, - Position = circle3OldPosition = new Vector2(304, 240) + Position = new Vector2(304, 240) }; EditorBeatmap.AddRange([circle1, circle2, circle3]); }); - AddStep("Select circles", () => + AddStep("store objects & new combo data", () => { - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); }); + AddStep("Select circles", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + AddStep("Reverse selection", () => { InputManager.PressKey(Key.LControl); @@ -107,26 +101,16 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.ReleaseKey(Key.LControl); }); - AddAssert("circle1 is at circle3 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, - () => Is.EqualTo(circle3OldPosition) - ); - - AddAssert("circle3 is at circle1 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).Position, - () => Is.EqualTo(circle1OldPosition) - ); - - AddAssert("circle3 is not a new combo", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).NewCombo, - () => Is.EqualTo(false) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } [Test] public void TestReverseSelectionCircleAndSlider() { - Vector2 circleOldPosition = default; + OsuHitObject[] objects = null!; + bool[] newCombos = null!; + Vector2 sliderHeadOldPosition = default; Vector2 sliderTailOldPosition = default; @@ -135,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor var circle = new HitCircle { StartTime = 0, - Position = circleOldPosition = new Vector2(208, 240) + Position = new Vector2(208, 240) }; var slider = new Slider { @@ -156,14 +140,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor EditorBeatmap.AddRange([circle, slider]); }); - AddStep("Select objects", () => + AddStep("store objects & new combo data", () => { - var circle = (HitCircle)EditorBeatmap.HitObjects[0]; - var slider = (Slider)EditorBeatmap.HitObjects[1]; - - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); }); + AddStep("Select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + AddStep("Reverse selection", () => { InputManager.PressKey(Key.LControl); @@ -171,10 +155,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.ReleaseKey(Key.LControl); }); - AddAssert("circle is at the same position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, - () => Is.EqualTo(circleOldPosition) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); AddAssert("Slider head is at slider tail", () => Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, sliderTailOldPosition) < 1); @@ -186,8 +168,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestReverseSelectionTwoCirclesAndSlider() { - Vector2 circle1OldPosition = default; - Vector2 circle2OldPosition = default; + OsuHitObject[] objects = null!; + bool[] newCombos = null!; Vector2 sliderHeadOldPosition = default; Vector2 sliderTailOldPosition = default; @@ -197,12 +179,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor var circle1 = new HitCircle { StartTime = 0, - Position = circle1OldPosition = new Vector2(208, 240) + Position = new Vector2(208, 240) }; var circle2 = new HitCircle { StartTime = 200, - Position = circle2OldPosition = new Vector2(256, 144) + Position = new Vector2(256, 144) }; var slider = new Slider { @@ -223,11 +205,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor EditorBeatmap.AddRange([circle1, circle2, slider]); }); - AddStep("Select objects", () => + AddStep("store objects & new combo data", () => { - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); }); + AddStep("Select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + AddStep("Reverse selection", () => { InputManager.PressKey(Key.LControl); @@ -235,15 +220,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.ReleaseKey(Key.LControl); }); - AddAssert("circle1 is at circle2 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, - () => Is.EqualTo(circle2OldPosition) - ); - - AddAssert("circle2 is at circle1 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, - () => Is.EqualTo(circle1OldPosition) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); AddAssert("Slider head is at slider tail", () => Vector2.Distance(EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, sliderTailOldPosition) < 1); @@ -255,57 +233,55 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [Test] public void TestReverseSelectionTwoCombos() { - Vector2 circle1OldPosition = default; - Vector2 circle2OldPosition = default; - Vector2 circle3OldPosition = default; - - Vector2 circle4OldPosition = default; - Vector2 circle5OldPosition = default; - Vector2 circle6OldPosition = default; + OsuHitObject[] objects = null!; + bool[] newCombos = null!; AddStep("Add circles", () => { var circle1 = new HitCircle { StartTime = 0, - Position = circle1OldPosition = new Vector2(216, 240) + Position = new Vector2(216, 240) }; var circle2 = new HitCircle { StartTime = 200, - Position = circle2OldPosition = new Vector2(120, 192) + Position = new Vector2(120, 192) }; var circle3 = new HitCircle { StartTime = 400, - Position = circle3OldPosition = new Vector2(216, 144) + Position = new Vector2(216, 144) }; var circle4 = new HitCircle { StartTime = 646, NewCombo = true, - Position = circle4OldPosition = new Vector2(296, 240) + Position = new Vector2(296, 240) }; var circle5 = new HitCircle { StartTime = 846, - Position = circle5OldPosition = new Vector2(392, 162) + Position = new Vector2(392, 162) }; var circle6 = new HitCircle { StartTime = 1046, - Position = circle6OldPosition = new Vector2(296, 144) + Position = new Vector2(296, 144) }; EditorBeatmap.AddRange([circle1, circle2, circle3, circle4, circle5, circle6]); }); - AddStep("Select circles", () => + AddStep("store objects & new combo data", () => { - EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects); + objects = getObjects().ToArray(); + newCombos = getObjectNewCombos().ToArray(); }); + AddStep("Select circles", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + AddStep("Reverse selection", () => { InputManager.PressKey(Key.LControl); @@ -313,35 +289,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor InputManager.ReleaseKey(Key.LControl); }); - AddAssert("circle1 is at circle6 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(0).Position, - () => Is.EqualTo(circle6OldPosition) - ); - - AddAssert("circle2 is at circle5 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(1).Position, - () => Is.EqualTo(circle5OldPosition) - ); - - AddAssert("circle3 is at circle4 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(2).Position, - () => Is.EqualTo(circle4OldPosition) - ); - - AddAssert("circle4 is at circle3 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(3).Position, - () => Is.EqualTo(circle3OldPosition) - ); - - AddAssert("circle5 is at circle2 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(4).Position, - () => Is.EqualTo(circle2OldPosition) - ); - - AddAssert("circle6 is at circle1 position", - () => EditorBeatmap.HitObjects.OfType().ElementAt(5).Position, - () => Is.EqualTo(circle1OldPosition) - ); + AddAssert("objects reversed", getObjects, () => Is.EqualTo(objects.Reverse())); + AddAssert("new combo positions preserved", getObjectNewCombos, () => Is.EqualTo(newCombos)); } + + private IEnumerable getObjects() => EditorBeatmap.HitObjects.OfType(); + + private IEnumerable getObjectNewCombos() => getObjects().Select(ho => ho.NewCombo); } } From 2f786ffc32d925d15d3417f696ac3a046dc2af80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Mar 2024 10:12:27 +0100 Subject: [PATCH 139/581] Simplify implementation --- .../Edit/CatchSelectionHandler.cs | 21 +++++++++---------- .../Edit/OsuSelectionHandler.cs | 21 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index f8fe9805e6..a2784126eb 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -77,13 +77,16 @@ namespace osu.Game.Rulesets.Catch.Edit public override bool HandleReverse() { var hitObjects = EditorBeatmap.SelectedHitObjects - .OfType() - .OrderBy(obj => obj.StartTime) - .ToList(); + .OfType() + .OrderBy(obj => obj.StartTime) + .ToList(); double selectionStartTime = SelectedItems.Min(h => h.StartTime); double selectionEndTime = SelectedItems.Max(h => h.GetEndTime()); + // the expectation is that even if the objects themselves are reversed temporally, + // the position of new combos in the selection should remain the same. + // preserve it for later before doing the reversal. var newComboOrder = hitObjects.Select(obj => obj.NewCombo).ToList(); foreach (var h in hitObjects) @@ -99,15 +102,11 @@ namespace osu.Game.Rulesets.Catch.Edit } } - // re-order objects again after flipping their times - hitObjects = [.. hitObjects.OrderBy(obj => obj.StartTime)]; + // re-order objects by start time again after reversing, and restore new combo flag positioning + hitObjects = hitObjects.OrderBy(obj => obj.StartTime).ToList(); - int i = 0; - foreach (bool newCombo in newComboOrder) - { - hitObjects[i].NewCombo = newCombo; - i++; - } + for (int i = 0; i < hitObjects.Count; ++i) + hitObjects[i].NewCombo = newComboOrder[i]; return true; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 0e889cab81..b33272968b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -79,15 +79,18 @@ namespace osu.Game.Rulesets.Osu.Edit public override bool HandleReverse() { var hitObjects = EditorBeatmap.SelectedHitObjects - .OfType() - .OrderBy(obj => obj.StartTime) - .ToList(); + .OfType() + .OrderBy(obj => obj.StartTime) + .ToList(); double endTime = hitObjects.Max(h => h.GetEndTime()); double startTime = hitObjects.Min(h => h.StartTime); bool moreThanOneObject = hitObjects.Count > 1; + // the expectation is that even if the objects themselves are reversed temporally, + // the position of new combos in the selection should remain the same. + // preserve it for later before doing the reversal. var newComboOrder = hitObjects.Select(obj => obj.NewCombo).ToList(); foreach (var h in hitObjects) @@ -102,15 +105,11 @@ namespace osu.Game.Rulesets.Osu.Edit } } - // re-order objects again after flipping their times - hitObjects = [.. hitObjects.OrderBy(obj => obj.StartTime)]; + // re-order objects by start time again after reversing, and restore new combo flag positioning + hitObjects = hitObjects.OrderBy(obj => obj.StartTime).ToList(); - int i = 0; - foreach (bool newCombo in newComboOrder) - { - hitObjects[i].NewCombo = newCombo; - i++; - } + for (int i = 0; i < hitObjects.Count; ++i) + hitObjects[i].NewCombo = newComboOrder[i]; return true; } From 5febd40bd9910f73e65d4bf37b08069833828e7d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 22:30:39 +0900 Subject: [PATCH 140/581] Add HP and AR to LegacyBeatmapConversionDifficultyInfo --- .../LegacyBeatmapConversionDifficultyInfo.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index 7d69069455..f8b8567305 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -18,6 +18,16 @@ namespace osu.Game.Rulesets.Scoring.Legacy /// public IRulesetInfo SourceRuleset { get; set; } = new RulesetInfo(); + /// + /// The beatmap drain rate. + /// + public float DrainRate { get; set; } + + /// + /// The beatmap approach rate. + /// + public float ApproachRate { get; set; } + /// /// The beatmap circle size. /// @@ -41,8 +51,6 @@ namespace osu.Game.Rulesets.Scoring.Legacy /// public int TotalObjectCount { get; set; } - float IBeatmapDifficultyInfo.DrainRate => 0; - float IBeatmapDifficultyInfo.ApproachRate => 0; double IBeatmapDifficultyInfo.SliderMultiplier => 0; double IBeatmapDifficultyInfo.SliderTickRate => 0; @@ -51,6 +59,8 @@ namespace osu.Game.Rulesets.Scoring.Legacy public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => new LegacyBeatmapConversionDifficultyInfo { SourceRuleset = beatmap.BeatmapInfo.Ruleset, + DrainRate = beatmap.Difficulty.DrainRate, + ApproachRate = beatmap.Difficulty.ApproachRate, CircleSize = beatmap.Difficulty.CircleSize, OverallDifficulty = beatmap.Difficulty.OverallDifficulty, EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration), @@ -60,6 +70,8 @@ namespace osu.Game.Rulesets.Scoring.Legacy public static LegacyBeatmapConversionDifficultyInfo FromBeatmapInfo(IBeatmapInfo beatmapInfo) => new LegacyBeatmapConversionDifficultyInfo { SourceRuleset = beatmapInfo.Ruleset, + DrainRate = beatmapInfo.Difficulty.DrainRate, + ApproachRate = beatmapInfo.Difficulty.ApproachRate, CircleSize = beatmapInfo.Difficulty.CircleSize, OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty, EndTimeObjectCount = beatmapInfo.EndTimeObjectCount, From 64399e9dd9841b7062e5f218c688424440c00b65 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 22:32:27 +0900 Subject: [PATCH 141/581] Refactor pattern generation to not require ManiaBeatmap --- .../Beatmaps/ManiaBeatmapConverter.cs | 30 ++++++++++++++----- .../Legacy/EndTimeObjectPatternGenerator.cs | 4 +-- .../Legacy/HitObjectPatternGenerator.cs | 6 ++-- .../Legacy/PathObjectPatternGenerator.cs | 4 +-- .../Patterns/Legacy/PatternGenerator.cs | 22 +++++--------- .../Beatmaps/Patterns/PatternGenerator.cs | 8 ++--- 6 files changed, 41 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index def22608d6..cc975c7def 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -27,8 +27,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private const int max_notes_for_density = 7; + /// + /// The total number of columns. + /// + public int TotalColumns => TargetColumns * (Dual ? 2 : 1); + + /// + /// The number of columns per-stage. + /// public int TargetColumns; + + /// + /// Whether to double the number of stages. + /// public bool Dual; + + /// + /// Whether the beatmap instantiated with is for the mania ruleset. + /// public readonly bool IsForCurrentRuleset; private readonly int originalTargetColumns; @@ -152,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The hit objects generated. private IEnumerable generateSpecific(HitObject original, IBeatmap originalBeatmap) { - var generator = new SpecificBeatmapPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); + var generator = new SpecificBeatmapPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern); foreach (var newPattern in generator.Generate()) { @@ -171,13 +187,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The hit objects generated. private IEnumerable generateConverted(HitObject original, IBeatmap originalBeatmap) { - Patterns.PatternGenerator conversion = null; + Patterns.PatternGenerator? conversion = null; switch (original) { case IHasPath: { - var generator = new PathObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); + var generator = new PathObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern); conversion = generator; var positionData = original as IHasPosition; @@ -195,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps case IHasDuration endTimeData: { - conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); + conversion = new EndTimeObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern); recordNote(endTimeData.EndTime, new Vector2(256, 192)); computeDensity(endTimeData.EndTime); @@ -206,7 +222,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { computeDensity(original.StartTime); - conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap); + conversion = new HitObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern, lastTime, lastPosition, density, lastStair); recordNote(original.StartTime, positionData.Position); break; @@ -231,8 +247,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator { - public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern) + : base(random, hitObject, beatmap, previousPattern, totalColumns) { } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 2265d3d347..52bb87ae19 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly int endTime; private readonly PatternType convertType; - public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern) + : base(random, hitObject, beatmap, previousPattern, totalColumns) { endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 27cb681300..ad45a3fb21 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly PatternType convertType; - public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, - PatternType lastStair, IBeatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, + double density, PatternType lastStair) + : base(random, hitObject, beatmap, previousPattern, totalColumns) { StairType = lastStair; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs index 4922915c7d..6d593a75e7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs @@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public PathObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) - : base(random, hitObject, beatmap, previousPattern, originalBeatmap) + public PathObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern) + : base(random, hitObject, beatmap, previousPattern, totalColumns) { convertType = PatternType.None; if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 77f93b4ef9..48b8778501 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -27,20 +27,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// protected readonly LegacyRandom Random; - /// - /// The beatmap which is being converted from. - /// - protected readonly IBeatmap OriginalBeatmap; - - protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) - : base(hitObject, beatmap, previousPattern) + protected PatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, Pattern previousPattern, int totalColumns) + : base(hitObject, beatmap, totalColumns, previousPattern) { ArgumentNullException.ThrowIfNull(random); - ArgumentNullException.ThrowIfNull(originalBeatmap); Random = random; - OriginalBeatmap = originalBeatmap; - RandomStart = TotalColumns == 8 ? 1 : 0; } @@ -104,17 +96,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (conversionDifficulty != null) return conversionDifficulty.Value; - HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault(); - HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault(); + HitObject lastObject = Beatmap.HitObjects.LastOrDefault(); + HitObject firstObject = Beatmap.HitObjects.FirstOrDefault(); // Drain time in seconds - int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - OriginalBeatmap.TotalBreakTime) / 1000); + int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - Beatmap.TotalBreakTime) / 1000); if (drainTime == 0) drainTime = 10000; - IBeatmapDifficultyInfo difficulty = OriginalBeatmap.Difficulty; - conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; + IBeatmapDifficultyInfo difficulty = Beatmap.Difficulty; + conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); return conversionDifficulty.Value; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index 3d3c35773b..8d98515fa4 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns @@ -25,11 +26,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// /// The beatmap which is a part of. /// - protected readonly ManiaBeatmap Beatmap; + protected readonly IBeatmap Beatmap; protected readonly int TotalColumns; - protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) + protected PatternGenerator(HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern) { ArgumentNullException.ThrowIfNull(hitObject); ArgumentNullException.ThrowIfNull(beatmap); @@ -38,8 +39,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns HitObject = hitObject; Beatmap = beatmap; PreviousPattern = previousPattern; - - TotalColumns = Beatmap.TotalColumns; + TotalColumns = totalColumns; } /// From 10edb5461490568a06f43c443dc6cc7f19b14c8a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 22:39:15 +0900 Subject: [PATCH 142/581] Add ability to query key count with mods --- .../Beatmaps/ManiaBeatmapConverter.cs | 93 ++++++++++--------- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 +- osu.Game/Rulesets/ILegacyRuleset.cs | 5 +- .../Carousel/DrawableCarouselBeatmap.cs | 7 +- .../Screens/Select/Details/AdvancedStats.cs | 2 +- 5 files changed, 61 insertions(+), 50 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index cc975c7def..8b339239a0 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Rulesets.Mania.Objects; using System; using System.Linq; @@ -14,6 +12,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Mania.Beatmaps.Patterns; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Utils; using osuTK; @@ -50,17 +49,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps private readonly int originalTargetColumns; // Internal for testing purposes - internal LegacyRandom Random { get; private set; } + internal readonly LegacyRandom Random; private Pattern lastPattern = new Pattern(); - private ManiaBeatmap beatmap; - public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) - : base(beatmap, ruleset) + : this(beatmap, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap), ruleset) { - IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); - TargetColumns = GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap)); + } + + private ManiaBeatmapConverter(IBeatmap? beatmap, LegacyBeatmapConversionDifficultyInfo difficulty, Ruleset ruleset) + : base(beatmap!, ruleset) + { + IsForCurrentRuleset = difficulty.SourceRuleset.Equals(ruleset.RulesetInfo); + Random = new LegacyRandom((int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate)); + TargetColumns = getColumnCount(difficulty); if (IsForCurrentRuleset && TargetColumns > ManiaRuleset.MAX_STAGE_KEYS) { @@ -69,51 +72,57 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } originalTargetColumns = TargetColumns; + + static int getColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty) + { + double roundedCircleSize = Math.Round(difficulty.CircleSize); + + if (difficulty.SourceRuleset.ShortName == ManiaRuleset.SHORT_NAME) + return (int)Math.Max(1, roundedCircleSize); + + double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); + + if (difficulty.TotalObjectCount > 0 && difficulty.EndTimeObjectCount >= 0) + { + int countSliderOrSpinner = difficulty.EndTimeObjectCount; + + // In osu!stable, this division appears as if it happens on floats, but due to release-mode + // optimisations, it actually ends up happening on doubles. + double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount; + + if (percentSpecialObjects < 0.2) + return 7; + if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5) + return roundedOverallDifficulty > 5 ? 7 : 6; + if (percentSpecialObjects > 0.6) + return roundedOverallDifficulty > 4 ? 5 : 4; + } + + return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); + } } - public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty) + public static int GetColumnCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) + => GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods); + + public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty, IReadOnlyList? mods = null) { - double roundedCircleSize = Math.Round(difficulty.CircleSize); + var converter = new ManiaBeatmapConverter(null, difficulty, new ManiaRuleset()); - if (difficulty.SourceRuleset.ShortName == ManiaRuleset.SHORT_NAME) - return (int)Math.Max(1, roundedCircleSize); - - double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); - - if (difficulty.TotalObjectCount > 0 && difficulty.EndTimeObjectCount >= 0) + if (mods != null) { - int countSliderOrSpinner = difficulty.EndTimeObjectCount; - - // In osu!stable, this division appears as if it happens on floats, but due to release-mode - // optimisations, it actually ends up happening on doubles. - double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount; - - if (percentSpecialObjects < 0.2) - return 7; - if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5) - return roundedOverallDifficulty > 5 ? 7 : 6; - if (percentSpecialObjects > 0.6) - return roundedOverallDifficulty > 4 ? 5 : 4; + foreach (var m in mods.OfType()) + m.ApplyToBeatmapConverter(converter); } - return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); + return converter.TotalColumns; } public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition); - protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) - { - IBeatmapDifficultyInfo difficulty = original.Difficulty; - - int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate); - Random = new LegacyRandom(seed); - - return base.ConvertBeatmap(original, cancellationToken); - } - protected override Beatmap CreateBeatmap() { - beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns), originalTargetColumns); + ManiaBeatmap beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns), originalTargetColumns); if (Dual) beatmap.Stages.Add(new StageDefinition(TargetColumns)); @@ -131,10 +140,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap); - - if (objects == null) - yield break; - foreach (ManiaHitObject obj in objects) yield return obj; } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3d4803f1e4..77168dca68 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -423,8 +423,8 @@ namespace osu.Game.Rulesets.Mania public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); - public int GetKeyCount(IBeatmapInfo beatmapInfo) - => ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo)); + public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) + => ManiaBeatmapConverter.GetColumnCount(beatmapInfo, mods); } public enum PlayfieldType diff --git a/osu.Game/Rulesets/ILegacyRuleset.cs b/osu.Game/Rulesets/ILegacyRuleset.cs index 18d86f477a..e116f7a1a3 100644 --- a/osu.Game/Rulesets/ILegacyRuleset.cs +++ b/osu.Game/Rulesets/ILegacyRuleset.cs @@ -1,7 +1,9 @@ // 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 osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring.Legacy; namespace osu.Game.Rulesets @@ -18,8 +20,7 @@ namespace osu.Game.Rulesets /// /// Retrieves the number of mania keys required to play the beatmap. /// - /// - int GetKeyCount(IBeatmapInfo beatmapInfo) => 0; + int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) => 0; ILegacyScoreSimulator CreateLegacyScoreSimulator(); } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 01e58d4ab2..2752beb645 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -28,6 +28,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; @@ -75,6 +76,9 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private IBindable ruleset { get; set; } = null!; + [Resolved] + private IBindable>? mods { get; set; } = null!; + private IBindable starDifficultyBindable = null!; private CancellationTokenSource? starDifficultyCancellationSource; @@ -185,6 +189,7 @@ namespace osu.Game.Screens.Select.Carousel base.LoadComplete(); ruleset.BindValueChanged(_ => updateKeyCount()); + mods?.BindValueChanged(_ => updateKeyCount()); } protected override void Selected() @@ -255,7 +260,7 @@ namespace osu.Game.Screens.Select.Carousel ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.Value.CreateInstance(); keyCountText.Alpha = 1; - keyCountText.Text = $"[{legacyRuleset.GetKeyCount(beatmapInfo)}K]"; + keyCountText.Text = $"[{legacyRuleset.GetKeyCount(beatmapInfo, mods?.Value)}K]"; } else keyCountText.Alpha = 0; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 1aba977f44..cb820f4da9 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -199,7 +199,7 @@ namespace osu.Game.Screens.Select.Details // For the time being, the key count is static no matter what, because: // a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering. // b) Using the difficulty adjustment mod to adjust OD doesn't have an effect on conversion. - int keyCount = baseDifficulty == null ? 0 : legacyRuleset.GetKeyCount(BeatmapInfo); + int keyCount = baseDifficulty == null ? 0 : legacyRuleset.GetKeyCount(BeatmapInfo, mods.Value); FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; FirstValue.Value = (keyCount, keyCount); From ce21235db495bb10e0f49763b7351f4f70fa51b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 22:47:43 +0900 Subject: [PATCH 143/581] Remove unused OriginalTargetColumns --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs | 6 ------ osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 6 +----- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 28cdf8907e..8222e5477d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -22,11 +22,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// public int TotalColumns => Stages.Sum(g => g.Columns); - /// - /// The total number of columns that were present in this before any user adjustments. - /// - public readonly int OriginalTotalColumns; - /// /// Creates a new . /// @@ -35,7 +30,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps public ManiaBeatmap(StageDefinition defaultStage, int? originalTotalColumns = null) { Stages.Add(defaultStage); - OriginalTotalColumns = originalTotalColumns ?? defaultStage.Columns; } public override IEnumerable GetStatistics() diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 8b339239a0..bed04a882f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -46,8 +46,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// public readonly bool IsForCurrentRuleset; - private readonly int originalTargetColumns; - // Internal for testing purposes internal readonly LegacyRandom Random; @@ -71,8 +69,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Dual = true; } - originalTargetColumns = TargetColumns; - static int getColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty) { double roundedCircleSize = Math.Round(difficulty.CircleSize); @@ -122,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override Beatmap CreateBeatmap() { - ManiaBeatmap beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns), originalTargetColumns); + ManiaBeatmap beatmap = new ManiaBeatmap(new StageDefinition(TargetColumns)); if (Dual) beatmap.Stages.Add(new StageDefinition(TargetColumns)); From c08a4898b27347201d63200d162718ddaf874068 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 22:58:39 +0900 Subject: [PATCH 144/581] Refactor score simulator to use GetColumnCount() --- .../Difficulty/ManiaLegacyScoreSimulator.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs index d9fd96ac6a..8a1b127265 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs @@ -51,13 +51,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty return multiplier; // Apply key mod multipliers. - int originalColumns = ManiaBeatmapConverter.GetColumnCount(difficulty); - int actualColumns = originalColumns; - - actualColumns = mods.OfType().SingleOrDefault()?.KeyCount ?? actualColumns; - if (mods.Any(m => m is ManiaModDualStages)) - actualColumns *= 2; + int actualColumns = ManiaBeatmapConverter.GetColumnCount(difficulty, mods); if (actualColumns > originalColumns) multiplier *= 0.9; From 9fd6449fd8b7c8b7a9019d1d3a25cb46a5b5562c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 23:02:25 +0900 Subject: [PATCH 145/581] Add mods to FilterCriteria, pass to ruleset method --- osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs | 5 ++--- .../NonVisual/Filtering/FilterMatchingTest.cs | 2 +- .../NonVisual/Filtering/FilterQueryParserTest.cs | 2 +- osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs | 3 ++- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- .../Select/Carousel/DrawableCarouselBeatmap.cs | 6 +++--- osu.Game/Screens/Select/FilterControl.cs | 13 ++++++++++--- osu.Game/Screens/Select/FilterCriteria.cs | 2 ++ 8 files changed, 22 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index 930ca217cd..07ed3ebd63 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -4,7 +4,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -14,9 +13,9 @@ namespace osu.Game.Rulesets.Mania { private FilterCriteria.OptionalRange keys; - public bool Matches(BeatmapInfo beatmapInfo) + public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) { - return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo))); + return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(beatmapInfo, criteria.Mods)); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs index c7a32ebbc4..78d8eabba7 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs @@ -309,7 +309,7 @@ namespace osu.Game.Tests.NonVisual.Filtering match = shouldMatch; } - public bool Matches(BeatmapInfo beatmapInfo) => match; + public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) => match; public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false; } } diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index ea14412f55..b0ceed45b9 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -502,7 +502,7 @@ namespace osu.Game.Tests.NonVisual.Filtering { public string? CustomValue { get; set; } - public bool Matches(BeatmapInfo beatmapInfo) => true; + public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) => true; public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) { diff --git a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs index dd2ad2cbfa..f926b04db4 100644 --- a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs +++ b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs @@ -18,11 +18,12 @@ namespace osu.Game.Rulesets.Filter /// in addition to the ones mandated by song select. /// /// The beatmap to test the criteria against. + /// The filter criteria. /// /// true if the beatmap matches the ruleset-specific custom filtering criteria, /// false otherwise. /// - bool Matches(BeatmapInfo beatmapInfo); + bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria); /// /// Attempts to parse a single custom keyword criterion, given by the user via the song select search box. diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 43461a48bb..8f38ae710c 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Select.Carousel match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; if (match && criteria.RulesetCriteria != null) - match &= criteria.RulesetCriteria.Matches(BeatmapInfo); + match &= criteria.RulesetCriteria.Matches(BeatmapInfo, criteria); return match; } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 2752beb645..f725d98342 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Select.Carousel private IBindable ruleset { get; set; } = null!; [Resolved] - private IBindable>? mods { get; set; } = null!; + private IBindable> mods { get; set; } = null!; private IBindable starDifficultyBindable = null!; private CancellationTokenSource? starDifficultyCancellationSource; @@ -189,7 +189,7 @@ namespace osu.Game.Screens.Select.Carousel base.LoadComplete(); ruleset.BindValueChanged(_ => updateKeyCount()); - mods?.BindValueChanged(_ => updateKeyCount()); + mods.BindValueChanged(_ => updateKeyCount()); } protected override void Selected() @@ -260,7 +260,7 @@ namespace osu.Game.Screens.Select.Carousel ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.Value.CreateInstance(); keyCountText.Alpha = 1; - keyCountText.Text = $"[{legacyRuleset.GetKeyCount(beatmapInfo, mods?.Value)}K]"; + keyCountText.Text = $"[{legacyRuleset.GetKeyCount(beatmapInfo, mods.Value)}K]"; } else keyCountText.Alpha = 0; diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 17297c9ebf..b19a7699c5 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Collections.Immutable; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -22,6 +23,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Filter; using osuTK; using osuTK.Graphics; @@ -65,6 +67,7 @@ namespace osu.Game.Screens.Select Sort = sortMode.Value, AllowConvertedBeatmaps = showConverted.Value, Ruleset = ruleset.Value, + Mods = mods.Value, CollectionBeatmapMD5Hashes = collectionDropdown.Current.Value?.Collection?.PerformRead(c => c.BeatmapMD5Hashes).ToImmutableHashSet() }; @@ -84,7 +87,7 @@ namespace osu.Game.Screens.Select base.ReceivePositionalInputAt(screenSpacePos) || sortTabs.ReceivePositionalInputAt(screenSpacePos); [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, IBindable parentRuleset, OsuConfigManager config) + private void load(OsuColour colours, OsuConfigManager config) { sortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); groupMode = config.GetBindable(OsuSetting.SongSelectGroupingMode); @@ -214,8 +217,8 @@ namespace osu.Game.Screens.Select config.BindWith(OsuSetting.DisplayStarsMaximum, maximumStars); maximumStars.ValueChanged += _ => updateCriteria(); - ruleset.BindTo(parentRuleset); ruleset.BindValueChanged(_ => updateCriteria()); + mods.BindValueChanged(_ => updateCriteria()); groupMode.BindValueChanged(_ => updateCriteria()); sortMode.BindValueChanged(_ => updateCriteria()); @@ -239,7 +242,11 @@ namespace osu.Game.Screens.Select searchTextBox.HoldFocus = true; } - private readonly IBindable ruleset = new Bindable(); + [Resolved] + private IBindable ruleset { get; set; } = null!; + + [Resolved] + private IBindable> mods { get; set; } = null!; private readonly Bindable showConverted = new Bindable(); private readonly Bindable minimumStars = new BindableDouble(); diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 01b0e9b7d9..190efd0fb0 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Rulesets; using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Filter; namespace osu.Game.Screens.Select @@ -50,6 +51,7 @@ namespace osu.Game.Screens.Select public OptionalTextFilter[] SearchTerms = Array.Empty(); public RulesetInfo? Ruleset; + public IReadOnlyList? Mods; public bool AllowConvertedBeatmaps; private string searchText = string.Empty; From 6e746a0fa053eb5c5c0d1b8cde1d0a1e1ba2c737 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Mar 2024 23:56:46 +0900 Subject: [PATCH 146/581] Fix carousel reoder on initial enter --- osu.Game/Screens/Select/FilterControl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index b19a7699c5..7b8b5393bd 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -218,7 +218,13 @@ namespace osu.Game.Screens.Select maximumStars.ValueChanged += _ => updateCriteria(); ruleset.BindValueChanged(_ => updateCriteria()); - mods.BindValueChanged(_ => updateCriteria()); + mods.BindValueChanged(_ => + { + // Mods are updated once by the mod select overlay when song select is entered, regardless of if there are any mods. + // Updating the criteria here so early triggers a re-ordering of panels on song select, via... some mechanism. + // Todo: Investigate/fix the above and remove this schedule. + Scheduler.AddOnce(updateCriteria); + }); groupMode.BindValueChanged(_ => updateCriteria()); sortMode.BindValueChanged(_ => updateCriteria()); From cbbb46cad87d40b3416e23c60f8dc5bf391004c9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Mar 2024 00:25:03 +0900 Subject: [PATCH 147/581] Update action versions in diffcalc workflow --- .github/workflows/diffcalc.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index 2ed176fe8d..7fd0f798cd 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -126,7 +126,7 @@ jobs: if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request }} steps: - name: Create comment - uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 with: comment_tag: ${{ env.EXECUTION_ID }} message: | @@ -253,7 +253,7 @@ jobs: - name: Restore cache id: restore-cache - uses: maxnowack/local-cache@038cc090b52e4f205fbc468bf5b0756df6f68775 # v1 + uses: maxnowack/local-cache@720e69c948191660a90aa1cf6a42fc4d2dacdf30 # v2 with: path: ${{ steps.query.outputs.DATA_NAME }}.tar.bz2 key: ${{ steps.query.outputs.DATA_NAME }} @@ -284,7 +284,7 @@ jobs: - name: Restore cache id: restore-cache - uses: maxnowack/local-cache@038cc090b52e4f205fbc468bf5b0756df6f68775 # v1 + uses: maxnowack/local-cache@720e69c948191660a90aa1cf6a42fc4d2dacdf30 # v2 with: path: ${{ steps.query.outputs.DATA_NAME }}.tar.bz2 key: ${{ steps.query.outputs.DATA_NAME }} @@ -358,7 +358,7 @@ jobs: steps: - name: Update comment on success if: ${{ needs.generator.result == 'success' }} - uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 with: comment_tag: ${{ env.EXECUTION_ID }} mode: upsert @@ -369,7 +369,7 @@ jobs: - name: Update comment on failure if: ${{ needs.generator.result == 'failure' }} - uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 with: comment_tag: ${{ env.EXECUTION_ID }} mode: upsert @@ -379,7 +379,7 @@ jobs: - name: Update comment on cancellation if: ${{ needs.generator.result == 'cancelled' }} - uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # v2.5.0 with: comment_tag: ${{ env.EXECUTION_ID }} mode: delete From c51a2e169d374b1cb820e2975def1cb2bbd902f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 12:19:06 +0800 Subject: [PATCH 148/581] Add test coverage of crash scenario --- osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs index e603f72bb8..24c9d1294f 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs @@ -74,6 +74,10 @@ namespace osu.Game.Tests.Visual.Menus }); AddStep("enter code", () => loginOverlay.ChildrenOfType().First().Text = "88800088"); assertAPIState(APIState.Online); + + AddStep("set failing", () => { dummyAPI.SetState(APIState.Failing); }); + AddStep("return to online", () => { dummyAPI.SetState(APIState.Online); }); + AddStep("clear handler", () => dummyAPI.HandleRequest = null); } From fef8afb833b7ddfd42b97260faff7593da514868 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 12:03:05 +0800 Subject: [PATCH 149/581] Fix double binding causing game crash after API enters failing state See https://sentry.ppy.sh/organizations/ppy/issues/33406/?alert_rule_id=4&alert_timestamp=1711655107332&alert_type=email&environment=production&project=2&referrer=alert_email --- osu.Game/Overlays/Login/LoginPanel.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 25bf612bc3..d5c7ed29b8 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Login [Resolved] private OsuColour colours { get; set; } = null!; - private UserDropdown dropdown = null!; + private UserDropdown? dropdown; /// /// Called to request a hide of a parent displaying this container. @@ -68,6 +68,14 @@ namespace osu.Game.Overlays.Login apiState.BindValueChanged(onlineStateChanged, true); } + protected override void LoadComplete() + { + base.LoadComplete(); + + userStatus.BindTo(api.LocalUser.Value.Status); + userStatus.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); + } + private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => { form = null; @@ -144,9 +152,6 @@ namespace osu.Game.Overlays.Login }, }; - userStatus.BindTo(api.LocalUser.Value.Status); - userStatus.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); - dropdown.Current.BindValueChanged(action => { switch (action.NewValue) @@ -171,6 +176,7 @@ namespace osu.Game.Overlays.Login break; } }, true); + break; } @@ -180,6 +186,9 @@ namespace osu.Game.Overlays.Login private void updateDropdownCurrent(UserStatus? status) { + if (dropdown == null) + return; + switch (status) { case UserStatus.Online: From d9cf5b5440ff3146e94d1c5da5c3c7c11bd53dd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 15:44:01 +0800 Subject: [PATCH 150/581] Fix bindable not being correctly re-bound across local user changes --- .../Visual/Menus/TestSceneLoginOverlay.cs | 12 +++++++++ osu.Game/Overlays/Login/LoginPanel.cs | 25 +++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs index 24c9d1294f..460d7814e0 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs @@ -15,6 +15,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Overlays; using osu.Game.Overlays.Login; using osu.Game.Overlays.Settings; +using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK.Input; @@ -72,13 +73,24 @@ namespace osu.Game.Tests.Visual.Menus return false; }); + AddStep("enter code", () => loginOverlay.ChildrenOfType().First().Text = "88800088"); assertAPIState(APIState.Online); + assertDropdownState(UserAction.Online); AddStep("set failing", () => { dummyAPI.SetState(APIState.Failing); }); AddStep("return to online", () => { dummyAPI.SetState(APIState.Online); }); AddStep("clear handler", () => dummyAPI.HandleRequest = null); + + assertDropdownState(UserAction.Online); + AddStep("change user state", () => dummyAPI.LocalUser.Value.Status.Value = UserStatus.DoNotDisturb); + assertDropdownState(UserAction.DoNotDisturb); + } + + private void assertDropdownState(UserAction state) + { + AddAssert($"dropdown state is {state}", () => loginOverlay.ChildrenOfType().First().Current.Value, () => Is.EqualTo(state)); } private void assertAPIState(APIState expected) => diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index d5c7ed29b8..a8adf4ce8c 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Settings; using osu.Game.Users; using osuTK; @@ -37,8 +38,10 @@ namespace osu.Game.Overlays.Login /// public Action? RequestHide; + private IBindable user = null!; + private readonly Bindable status = new Bindable(); + private readonly IBindable apiState = new Bindable(); - private readonly Bindable userStatus = new Bindable(); [Resolved] private IAPIProvider api { get; set; } = null!; @@ -61,19 +64,21 @@ namespace osu.Game.Overlays.Login AutoSizeAxes = Axes.Y; } - [BackgroundDependencyLoader] - private void load() - { - apiState.BindTo(api.State); - apiState.BindValueChanged(onlineStateChanged, true); - } - protected override void LoadComplete() { base.LoadComplete(); - userStatus.BindTo(api.LocalUser.Value.Status); - userStatus.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); + apiState.BindTo(api.State); + apiState.BindValueChanged(onlineStateChanged, true); + + user = api.LocalUser.GetBoundCopy(); + user.BindValueChanged(u => + { + status.UnbindBindings(); + status.BindTo(u.NewValue.Status); + }, true); + + status.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); } private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => From 233b9eb1723fb44a26c1a65c1d583e3e6f87f7b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 17:11:55 +0800 Subject: [PATCH 151/581] Avoid reporting an import as successful when all beatmaps failed to import --- osu.Game/Beatmaps/BeatmapImporter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 5ff3ab64b2..9ca0aafddd 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -434,6 +434,9 @@ namespace osu.Game.Beatmaps } } + if (!beatmaps.Any()) + throw new ArgumentException($"No valid beatmap files found in the beatmap archive."); + return beatmaps; } } From df4a28db915653f79603ef78ea44af48d781391e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 17:32:20 +0800 Subject: [PATCH 152/581] Fix failing test due to missing ruleset store --- osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs b/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs index 5f722e381c..016928c6d6 100644 --- a/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs +++ b/osu.Game.Tests/Database/LegacyBeatmapImporterTest.cs @@ -12,6 +12,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO; +using osu.Game.Rulesets; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Database @@ -77,6 +78,7 @@ namespace osu.Game.Tests.Database { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) using (var tmpStorage = new TemporaryNativeStorage("stable-songs-folder")) + using (new RealmRulesetStore(realm, storage)) { var stableStorage = new StableStorage(tmpStorage.GetFullPath(""), host); var songsStorage = stableStorage.GetStorageForDirectory(StableStorage.STABLE_DEFAULT_SONGS_PATH); From 2d3b273974de4cb85dea652f3915a37f7ee0451e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Mar 2024 10:36:17 +0100 Subject: [PATCH 153/581] Remove redundant string interpolation --- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 9ca0aafddd..2137f33e77 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -435,7 +435,7 @@ namespace osu.Game.Beatmaps } if (!beatmaps.Any()) - throw new ArgumentException($"No valid beatmap files found in the beatmap archive."); + throw new ArgumentException("No valid beatmap files found in the beatmap archive."); return beatmaps; } From e06df34a1c7f57ffec818be87264c4e82b61e508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Mar 2024 11:16:31 +0100 Subject: [PATCH 154/581] Apply partial fade on pp display on results screen when score will not give pp --- .../TestSceneExpandedPanelMiddleContent.cs | 37 +++++++++++++++++++ osu.Game/Localisation/ResultsScreenStrings.cs | 24 ++++++++++++ .../Statistics/PerformanceStatistic.cs | 32 ++++++++++++++-- 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Localisation/ResultsScreenStrings.cs diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index d97946a1d5..9f7726313a 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -14,13 +14,16 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; using osu.Game.Models; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking.Expanded; +using osu.Game.Screens.Ranking.Expanded.Statistics; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; using osuTK; @@ -67,6 +70,40 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("play time displayed", () => this.ChildrenOfType().Any()); } + [Test] + public void TestPPShownAsProvisionalWhenBeatmapHasNoLeaderboard() + { + AddStep("show example score", () => + { + var beatmap = createTestBeatmap(new RealmUser()); + beatmap.Status = BeatmapOnlineStatus.Graveyard; + showPanel(TestResources.CreateTestScoreInfo(beatmap)); + }); + + AddAssert("pp display faded out", () => + { + var ppDisplay = this.ChildrenOfType().Single(); + return ppDisplay.Alpha == 0.5 && ppDisplay.TooltipText == ResultsScreenStrings.NoPPForUnrankedBeatmaps; + }); + } + + [Test] + public void TestPPShownAsProvisionalWhenUnrankedModsArePresent() + { + AddStep("show example score", () => + { + var score = TestResources.CreateTestScoreInfo(createTestBeatmap(new RealmUser())); + score.Mods = score.Mods.Append(new OsuModDifficultyAdjust()).ToArray(); + showPanel(score); + }); + + AddAssert("pp display faded out", () => + { + var ppDisplay = this.ChildrenOfType().Single(); + return ppDisplay.Alpha == 0.5 && ppDisplay.TooltipText == ResultsScreenStrings.NoPPForUnrankedMods; + }); + } + [Test] public void TestWithDefaultDate() { diff --git a/osu.Game/Localisation/ResultsScreenStrings.cs b/osu.Game/Localisation/ResultsScreenStrings.cs new file mode 100644 index 0000000000..54e7717af9 --- /dev/null +++ b/osu.Game/Localisation/ResultsScreenStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class ResultsScreenStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.ResultsScreen"; + + /// + /// "Performance points are not granted for this score because the beatmap is not ranked." + /// + public static LocalisableString NoPPForUnrankedBeatmaps => new TranslatableString(getKey(@"no_pp_for_unranked_beatmaps"), @"Performance points are not granted for this score because the beatmap is not ranked."); + + /// + /// "Performance points are not granted for this score because of unranked mods." + /// + public static LocalisableString NoPPForUnrankedMods => new TranslatableString(getKey(@"no_pp_for_unranked_mods"), @"Performance points are not granted for this score because of unranked mods."); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 22c1e26d43..0a9c68eafc 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -4,20 +4,26 @@ #nullable disable using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; +using osu.Game.Localisation; namespace osu.Game.Screens.Ranking.Expanded.Statistics { - public partial class PerformanceStatistic : StatisticDisplay + public partial class PerformanceStatistic : StatisticDisplay, IHasTooltip { + public LocalisableString TooltipText { get; private set; } + private readonly ScoreInfo score; private readonly Bindable performance = new Bindable(); @@ -37,7 +43,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { if (score.PP.HasValue) { - setPerformanceValue(score.PP.Value); + setPerformanceValue(score, score.PP.Value); } else { @@ -52,15 +58,33 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics var result = await performanceCalculator.CalculateAsync(score, attributes.Value.Attributes, cancellationToken ?? default).ConfigureAwait(false); - Schedule(() => setPerformanceValue(result.Total)); + Schedule(() => setPerformanceValue(score, result.Total)); }, cancellationToken ?? default); } } - private void setPerformanceValue(double? pp) + private void setPerformanceValue(ScoreInfo scoreInfo, double? pp) { if (pp.HasValue) + { performance.Value = (int)Math.Round(pp.Value, MidpointRounding.AwayFromZero); + + if (!scoreInfo.BeatmapInfo!.Status.GrantsPerformancePoints()) + { + Alpha = 0.5f; + TooltipText = ResultsScreenStrings.NoPPForUnrankedBeatmaps; + } + else if (scoreInfo.Mods.Any(m => !m.Ranked)) + { + Alpha = 0.5f; + TooltipText = ResultsScreenStrings.NoPPForUnrankedMods; + } + else + { + Alpha = 1f; + TooltipText = default; + } + } } public override void Appear() From c21805589eb2014bd07e6386b9398734e9a99dcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Mar 2024 22:40:04 +0800 Subject: [PATCH 155/581] Fix taiko mascot size not matching stable --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 7b1e31112e..e863c4c2e4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Taiko.UI InternalChild = textureAnimation = createTextureAnimation(state).With(animation => { animation.Origin = animation.Anchor = Anchor.BottomLeft; - animation.Scale = new Vector2(0.51f); // close enough to stable + // matches stable (https://github.com/peppy/osu-stable-reference/blob/054d0380c19aa5972be176d9d242ceb0e1630ae6/osu!/GameModes/Play/Rulesets/Taiko/TaikoMascot.cs#L34) + animation.Scale = new Vector2(0.6f); }); RelativeSizeAxes = Axes.Both; From 51f79c33e1c93ecb4cbf71e5de820941a0fa622e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Mar 2024 23:33:04 +0300 Subject: [PATCH 156/581] Fix URL pointing to non-existent commit --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index e863c4c2e4..90f7782aba 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.UI InternalChild = textureAnimation = createTextureAnimation(state).With(animation => { animation.Origin = animation.Anchor = Anchor.BottomLeft; - // matches stable (https://github.com/peppy/osu-stable-reference/blob/054d0380c19aa5972be176d9d242ceb0e1630ae6/osu!/GameModes/Play/Rulesets/Taiko/TaikoMascot.cs#L34) + // matches stable (https://github.com/peppy/osu-stable-reference/blob/e53980dd76857ee899f66ce519ba1597e7874f28/osu!/GameModes/Play/Rulesets/Taiko/TaikoMascot.cs#L34) animation.Scale = new Vector2(0.6f); }); From 8d6358a138605828c5260b4efca907049ed73e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Sat, 30 Mar 2024 16:02:31 +0700 Subject: [PATCH 157/581] Fix editor rotation allowing spinner only bug --- osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs index 1998e02a5c..d48bc6a90b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit { var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects); CanRotateSelectionOrigin.Value = quad.Width > 0 || quad.Height > 0; - CanRotatePlayfieldOrigin.Value = selectedItems.Any(); + CanRotatePlayfieldOrigin.Value = selectedMovableObjects.Any(); } private OsuHitObject[]? objectsInRotation; From 5d497ba4a8ada3ae5ad733ed6d856328b39a9576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Sat, 30 Mar 2024 16:04:22 +0700 Subject: [PATCH 158/581] Simplify TooltipText for EditorRadioButton --- .../Edit/PreciseRotationPopover.cs | 8 +++++++- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 8 +++++++- .../Components/RadioButtons/EditorRadioButton.cs | 2 +- .../Edit/Components/RadioButtons/RadioButton.cs | 13 +++---------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index 2cf6799279..6c29184be4 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -68,7 +68,13 @@ namespace osu.Game.Rulesets.Osu.Edit } } }; - selectionCentreButton.TooltipTextWhenDisabled = "We can't rotate a circle around itself! Can we?"; + selectionCentreButton.Selected.DisabledChanged += (isDisabled) => + { + if (isDisabled) + selectionCentreButton.TooltipText = "We can't rotate a circle around itself! Can we?"; + else + selectionCentreButton.TooltipText = string.Empty; + }; } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index bc8de7f4b2..09bac7a791 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -214,7 +214,13 @@ namespace osu.Game.Rulesets.Edit foreach (var item in toolboxCollection.Items) { - item.TooltipTextWhenDisabled = "Add at least one timing point first!"; + item.Selected.DisabledChanged += (isDisabled) => + { + if (isDisabled) + item.TooltipText = "Add at least one timing point first!"; + else + item.TooltipText = string.Empty; + }; } TernaryStates = CreateTernaryButtons().ToArray(); diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs index 9d1f87e1e0..29bb24eb43 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/EditorRadioButton.cs @@ -94,6 +94,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons X = 40f }; - public LocalisableString TooltipText => Enabled.Value ? Button.TooltipTextWhenEnabled : Button.TooltipTextWhenDisabled; + public LocalisableString TooltipText => Button.TooltipText; } } diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index 2d1416c9c6..f49fc6f6ab 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -16,16 +16,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// public readonly BindableBool Selected; - /// - /// Tooltip text that will be shown on hover if button is enabled. - /// - public LocalisableString TooltipTextWhenEnabled { get; set; } = string.Empty; - - /// - /// Tooltip text that will be shown on hover if button is disabled. - /// - public LocalisableString TooltipTextWhenDisabled { get; set; } = string.Empty; - /// /// The item related to this button. /// @@ -62,5 +52,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// Deselects this . /// public void Deselect() => Selected.Value = false; + + // Tooltip text that will be shown when hovered over + public LocalisableString TooltipText { get; set; } = string.Empty; } } From 6f782266b51b717e996cc258b05da8fb737533d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Sat, 30 Mar 2024 17:03:40 +0700 Subject: [PATCH 159/581] Fix inspection --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 7 ++----- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index 6c29184be4..70441b33dd 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -68,12 +68,9 @@ namespace osu.Game.Rulesets.Osu.Edit } } }; - selectionCentreButton.Selected.DisabledChanged += (isDisabled) => + selectionCentreButton.Selected.DisabledChanged += isDisabled => { - if (isDisabled) - selectionCentreButton.TooltipText = "We can't rotate a circle around itself! Can we?"; - else - selectionCentreButton.TooltipText = string.Empty; + selectionCentreButton.TooltipText = isDisabled ? "We can't rotate a circle around itself! Can we?" : string.Empty; }; } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 09bac7a791..4d92a08bed 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -214,12 +214,9 @@ namespace osu.Game.Rulesets.Edit foreach (var item in toolboxCollection.Items) { - item.Selected.DisabledChanged += (isDisabled) => + item.Selected.DisabledChanged += isDisabled => { - if (isDisabled) - item.TooltipText = "Add at least one timing point first!"; - else - item.TooltipText = string.Empty; + item.TooltipText = isDisabled ? "Add at least one timing point first!" : string.Empty; }; } From b445e27ad6bfae93244b89acc1544aef01978bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= Date: Sat, 30 Mar 2024 17:54:27 +0700 Subject: [PATCH 160/581] Aggregate two CanRotate bindable for enabling the Rotate button --- osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs index 19590e9b6e..3e2cbe9d60 100644 --- a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs @@ -51,9 +51,17 @@ namespace osu.Game.Rulesets.Osu.Edit { base.LoadComplete(); + // aggregate two values into canRotate + RotationHandler.CanRotatePlayfieldOrigin.BindValueChanged(_ => updateCanRotateAggregate()); + RotationHandler.CanRotateSelectionOrigin.BindValueChanged(_ => updateCanRotateAggregate()); + + void updateCanRotateAggregate() + { + canRotate.Value = RotationHandler.CanRotatePlayfieldOrigin.Value || RotationHandler.CanRotateSelectionOrigin.Value; + } + // bindings to `Enabled` on the buttons are decoupled on purpose // due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set. - canRotate.BindTo(RotationHandler.CanRotatePlayfieldOrigin); canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true); } From 54472e6452c61671d487f59d6e61af89daaa6d59 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 31 Mar 2024 01:20:27 +0300 Subject: [PATCH 161/581] Decouple GlowingDrawable from GlowingSpriteText --- osu.Game/Graphics/Sprites/GlowingDrawable.cs | 41 +++++++++++++++++++ .../Graphics/Sprites/GlowingSpriteText.cs | 39 ++++-------------- 2 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 osu.Game/Graphics/Sprites/GlowingDrawable.cs diff --git a/osu.Game/Graphics/Sprites/GlowingDrawable.cs b/osu.Game/Graphics/Sprites/GlowingDrawable.cs new file mode 100644 index 0000000000..10085ad38b --- /dev/null +++ b/osu.Game/Graphics/Sprites/GlowingDrawable.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Graphics.Sprites +{ + public abstract partial class GlowingDrawable : BufferedContainer + { + // Inflate draw quad to prevent glow from trimming at the edges. + // Padding won't suffice since it will affect drawable position in cases when it's not centered. + protected override Quad ComputeScreenSpaceDrawQuad() + => base.ComputeScreenSpaceDrawQuad().AABBFloat.Inflate(new Vector2(Blur.KernelSize(BlurSigma.X), Blur.KernelSize(BlurSigma.Y))); + + public ColourInfo GlowColour + { + get => EffectColour; + set + { + EffectColour = value; + BackgroundColour = value.MultiplyAlpha(0f); + } + } + + protected GlowingDrawable() + : base(cachedFrameBuffer: true) + { + AutoSizeAxes = Axes.Both; + RedrawOnScale = false; + DrawOriginal = true; + Child = CreateDrawable(); + } + + protected abstract Drawable CreateDrawable(); + } +} diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 669c5da01e..3ac13bf862 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -4,24 +4,17 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Framework.Utils; using osuTK; namespace osu.Game.Graphics.Sprites { - public partial class GlowingSpriteText : BufferedContainer, IHasText + public partial class GlowingSpriteText : GlowingDrawable, IHasText { private const float blur_sigma = 3f; - // Inflate draw quad to prevent glow from trimming at the edges. - // Padding won't suffice since it will affect text position in cases when it's not centered. - protected override Quad ComputeScreenSpaceDrawQuad() => base.ComputeScreenSpaceDrawQuad().AABBFloat.Inflate(Blur.KernelSize(blur_sigma)); - - private readonly OsuSpriteText text; + private OsuSpriteText text = null!; public LocalisableString Text { @@ -47,16 +40,6 @@ namespace osu.Game.Graphics.Sprites set => text.Colour = value; } - public ColourInfo GlowColour - { - get => EffectColour; - set - { - EffectColour = value; - BackgroundColour = value.MultiplyAlpha(0f); - } - } - public Vector2 Spacing { get => text.Spacing; @@ -76,20 +59,16 @@ namespace osu.Game.Graphics.Sprites } public GlowingSpriteText() - : base(cachedFrameBuffer: true) { - AutoSizeAxes = Axes.Both; BlurSigma = new Vector2(blur_sigma); - RedrawOnScale = false; - DrawOriginal = true; EffectBlending = BlendingParameters.Additive; - EffectPlacement = EffectPlacement.InFront; - Child = text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shadow = false, - }; } + + protected override Drawable CreateDrawable() => text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shadow = false, + }; } } From 58a68e94af79205aa13989fb8b896c9d27c0f9c0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 31 Mar 2024 01:30:08 +0300 Subject: [PATCH 162/581] Simplify glowing icons in break overlay --- osu.Game/Screens/Play/Break/BlurredIcon.cs | 44 ++--------------- osu.Game/Screens/Play/Break/GlowIcon.cs | 57 ++++++++-------------- 2 files changed, 23 insertions(+), 78 deletions(-) diff --git a/osu.Game/Screens/Play/Break/BlurredIcon.cs b/osu.Game/Screens/Play/Break/BlurredIcon.cs index 2bf59ea63b..9cd617d3e3 100644 --- a/osu.Game/Screens/Play/Break/BlurredIcon.cs +++ b/osu.Game/Screens/Play/Break/BlurredIcon.cs @@ -1,52 +1,16 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . 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.Sprites; -using osu.Game.Graphics; -using osuTK; namespace osu.Game.Screens.Play.Break { - public partial class BlurredIcon : BufferedContainer + public partial class BlurredIcon : GlowIcon { - private readonly SpriteIcon icon; - - public IconUsage Icon - { - set => icon.Icon = value; - get => icon.Icon; - } - - public override Vector2 Size - { - set - { - icon.Size = value; - base.Size = value + BlurSigma * 5; - ForceRedraw(); - } - get => base.Size; - } - public BlurredIcon() - : base(cachedFrameBuffer: true) { - RelativePositionAxes = Axes.X; - Child = icon = new SpriteIcon - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Shadow = false, - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Colour = colours.BlueLighter; + EffectBlending = BlendingParameters.Additive; + DrawOriginal = false; } } } diff --git a/osu.Game/Screens/Play/Break/GlowIcon.cs b/osu.Game/Screens/Play/Break/GlowIcon.cs index 8e2b9da0ad..a68cfdac42 100644 --- a/osu.Game/Screens/Play/Break/GlowIcon.cs +++ b/osu.Game/Screens/Play/Break/GlowIcon.cs @@ -3,64 +3,45 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osuTK; namespace osu.Game.Screens.Play.Break { - public partial class GlowIcon : Container + public partial class GlowIcon : GlowingDrawable { - private readonly SpriteIcon spriteIcon; - private readonly BlurredIcon blurredIcon; - - public override Vector2 Size - { - get => base.Size; - set - { - blurredIcon.Size = spriteIcon.Size = value; - blurredIcon.ForceRedraw(); - } - } - - public Vector2 BlurSigma - { - get => blurredIcon.BlurSigma; - set => blurredIcon.BlurSigma = value; - } + private SpriteIcon icon = null!; public IconUsage Icon { - get => spriteIcon.Icon; - set => spriteIcon.Icon = blurredIcon.Icon = value; + set => icon.Icon = value; + get => icon.Icon; + } + + public new Vector2 Size + { + set => icon.Size = value; + get => icon.Size; } public GlowIcon() { RelativePositionAxes = Axes.X; - AutoSizeAxes = Axes.Both; - Children = new Drawable[] - { - blurredIcon = new BlurredIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - spriteIcon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shadow = false, - } - }; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - blurredIcon.Colour = colours.Blue; + GlowColour = colours.BlueLighter; } + + protected override Drawable CreateDrawable() => icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Shadow = false, + }; } } From 11b113580496fd979bfa083573e0e17ac6cb8f64 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 31 Mar 2024 01:55:34 +0300 Subject: [PATCH 163/581] Adjust blurred icons position due to size handling differences --- osu.Game/Screens/Play/Break/BreakArrows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Break/BreakArrows.cs b/osu.Game/Screens/Play/Break/BreakArrows.cs index 41277c7557..40474a7137 100644 --- a/osu.Game/Screens/Play/Break/BreakArrows.cs +++ b/osu.Game/Screens/Play/Break/BreakArrows.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Play.Break private const int blurred_icon_blur_sigma = 20; private const int blurred_icon_size = 130; - private const float blurred_icon_final_offset = 0.35f; + private const float blurred_icon_final_offset = 0.38f; private const float blurred_icon_offscreen_offset = 0.7f; private readonly GlowIcon leftGlowIcon; From 450e7016bcbf2e778910f6114da6688140d3cbd5 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Sat, 30 Mar 2024 21:23:05 -0300 Subject: [PATCH 164/581] Bind `StackHeight` changes to visual update methods --- .../TestScenePathControlPointVisualiser.cs | 48 +++++++++++++++++++ .../PathControlPointConnectionPiece.cs | 2 + .../Components/PathControlPointPiece.cs | 2 + 3 files changed, 52 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 2b53554ed1..0c12e6fb21 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -172,6 +172,54 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPathType(4, null); } + [Test] + public void TestStackingUpdatesPointsPosition() + { + createVisualiser(true); + + Vector2[] points = [ + new Vector2(200), + new Vector2(300), + new Vector2(500, 300), + new Vector2(700, 200), + new Vector2(500, 100) + ]; + + foreach (var point in points) addControlPointStep(point); + + AddStep("apply stacking", () => slider.StackHeightBindable.Value += 1); + + for (int i = 0; i < points.Length; i++) + addAssertPointPositionChanged(points, i); + } + + [Test] + public void TestStackingUpdatesConnectionPosition() + { + createVisualiser(true); + + Vector2 connectionPosition = default!; + + addControlPointStep(connectionPosition = new Vector2(300)); + addControlPointStep(new Vector2(600)); + + // Apply a big number in stacking so the person running the test can clearly see if it fails + AddStep("apply stacking", () => slider.StackHeightBindable.Value += 10); + + AddAssert($"Connection at {connectionPosition} changed", + () => visualiser.Connections[0].Position, + () => !Is.EqualTo(connectionPosition) + ); + } + + private void addAssertPointPositionChanged(Vector2[] points, int index) + { + AddAssert($"Point at {points.ElementAt(index)} changed", + () => visualiser.Pieces[index].Position, + () => !Is.EqualTo(points.ElementAt(index)) + ); + } + private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection) { Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 7e7d653dbd..56dc16dd95 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -56,6 +56,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components pathVersion = hitObject.Path.Version.GetBoundCopy(); pathVersion.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); + hitObject.StackHeightBindable.BindValueChanged(_ => updateConnectingPath()); + updateConnectingPath(); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index e741d67e3b..ee306fb6d7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components hitObjectScale = hitObject.ScaleBindable.GetBoundCopy(); hitObjectScale.BindValueChanged(_ => updateMarkerDisplay()); + hitObject.StackHeightBindable.BindValueChanged(_ => updateMarkerDisplay()); + IsSelected.BindValueChanged(_ => updateMarkerDisplay()); updateMarkerDisplay(); From 86def7e263ff0bc811a9f7cd4b51c4d7ee1d7129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= <32929093+honguyenminh@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:00:47 +0700 Subject: [PATCH 165/581] Change editor rotate button disabled tooltip text Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index 70441b33dd..da50233920 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; selectionCentreButton.Selected.DisabledChanged += isDisabled => { - selectionCentreButton.TooltipText = isDisabled ? "We can't rotate a circle around itself! Can we?" : string.Empty; + selectionCentreButton.TooltipText = isDisabled ? "Select more than one circles to perform rotation." : string.Empty; }; } From 19f0caa0f363427d0df39e5f9eb7e34c822f2699 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Sun, 31 Mar 2024 13:39:19 -0300 Subject: [PATCH 166/581] Fix wrong formatting in array creation --- .../Editor/TestScenePathControlPointVisualiser.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 0c12e6fb21..335ccb5280 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -177,7 +177,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - Vector2[] points = [ + Vector2[] points = + [ new Vector2(200), new Vector2(300), new Vector2(500, 300), From 7615c71efef1742e2c19620086964474d03a69f4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 1 Apr 2024 15:28:20 +0900 Subject: [PATCH 167/581] Fix inspection --- .../Editor/TestScenePathControlPointVisualiser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 335ccb5280..0ca30e00bc 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -199,8 +199,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - Vector2 connectionPosition = default!; - + Vector2 connectionPosition; addControlPointStep(connectionPosition = new Vector2(300)); addControlPointStep(new Vector2(600)); From 099ad22a92658f8bc016a1abe72fb2b13fb99da6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 1 Apr 2024 15:31:34 +0900 Subject: [PATCH 168/581] Use local bindable instead Binding events directly to an external bindable will cause that bindable to hold a permanent reference to the current object. We use `GetBoundCopy()` or otherwise a local bindable + `.BindTo()` to create a weak-referenced copy of the target bindable. When the local bindable's lifetime expires, so does the external bindable's reference to it. --- .../Sliders/Components/PathControlPointConnectionPiece.cs | 4 +++- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 56dc16dd95..9b3d8fc7a7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable hitObjectPosition; private IBindable pathVersion; + private IBindable stackHeight; public PathControlPointConnectionPiece(T hitObject, int controlPointIndex) { @@ -56,7 +57,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components pathVersion = hitObject.Path.Version.GetBoundCopy(); pathVersion.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); - hitObject.StackHeightBindable.BindValueChanged(_ => updateConnectingPath()); + stackHeight = hitObject.StackHeightBindable.GetBoundCopy(); + stackHeight.BindValueChanged(_ => updateConnectingPath()); updateConnectingPath(); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index ee306fb6d7..c6e05d3ca3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable hitObjectPosition; private IBindable hitObjectScale; + private IBindable stackHeight; public PathControlPointPiece(T hitObject, PathControlPoint controlPoint) { @@ -105,7 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components hitObjectScale = hitObject.ScaleBindable.GetBoundCopy(); hitObjectScale.BindValueChanged(_ => updateMarkerDisplay()); - hitObject.StackHeightBindable.BindValueChanged(_ => updateMarkerDisplay()); + stackHeight = hitObject.StackHeightBindable.GetBoundCopy(); + stackHeight.BindValueChanged(_ => updateMarkerDisplay()); IsSelected.BindValueChanged(_ => updateMarkerDisplay()); From d12a2e7df7131fea37974bbb0707705cc2f0e977 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 1 Apr 2024 17:02:02 +0900 Subject: [PATCH 169/581] Replace schedule with SequenceEqual() --- osu.Game/Screens/Select/FilterControl.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 7b8b5393bd..0bfd927234 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -218,12 +219,16 @@ namespace osu.Game.Screens.Select maximumStars.ValueChanged += _ => updateCriteria(); ruleset.BindValueChanged(_ => updateCriteria()); - mods.BindValueChanged(_ => + mods.BindValueChanged(m => { - // Mods are updated once by the mod select overlay when song select is entered, regardless of if there are any mods. + // Mods are updated once by the mod select overlay when song select is entered, + // regardless of if there are any mods or any changes have taken place. // Updating the criteria here so early triggers a re-ordering of panels on song select, via... some mechanism. - // Todo: Investigate/fix the above and remove this schedule. - Scheduler.AddOnce(updateCriteria); + // Todo: Investigate/fix and potentially remove this. + if (m.NewValue.SequenceEqual(m.OldValue)) + return; + + updateCriteria(); }); groupMode.BindValueChanged(_ => updateCriteria()); From 8e0ca11d1cca1571b0d0efd61e0322b618333e5d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 1 Apr 2024 17:02:32 +0900 Subject: [PATCH 170/581] Fully qualify LegacyBeatmapConversionDifficultyInfo --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 3 --- osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs | 3 ++- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index bed04a882f..39ee3d209b 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -98,9 +98,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps } } - public static int GetColumnCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) - => GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods); - public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty, IReadOnlyList? mods = null) { var converter = new ManiaBeatmapConverter(null, difficulty, new ManiaRuleset()); diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index 07ed3ebd63..ea7eb5b8f0 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -4,6 +4,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Mania public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) { - return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(beatmapInfo, criteria.Mods)); + return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), criteria.Mods)); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 77168dca68..b5614e2b56 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -424,7 +424,7 @@ namespace osu.Game.Rulesets.Mania public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) - => ManiaBeatmapConverter.GetColumnCount(beatmapInfo, mods); + => ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods); } public enum PlayfieldType From 4806ea54f1a55c1220c0772d2b385f674a06661d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 1 Apr 2024 17:22:50 +0900 Subject: [PATCH 171/581] Only optimise Catmull segments in osu ruleset --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 203e829180..cc3ffd376e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); - private readonly SliderPath path = new SliderPath(); + private readonly SliderPath path = new SliderPath { OptimiseCatmull = true }; public SliderPath Path { diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 5398d6c45f..e8e769e3fa 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -134,6 +134,24 @@ namespace osu.Game.Rulesets.Objects } } + private bool optimiseCatmull; + + /// + /// Whether to optimise Catmull path segments, usually resulting in removing bulbs around stacked knots. + /// + /// + /// This changes the path shape and should therefore not be used. + /// + public bool OptimiseCatmull + { + get => optimiseCatmull; + set + { + optimiseCatmull = value; + invalidate(); + } + } + /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) /// to 1 (end of the slider) and stores the generated path in the given list. @@ -280,7 +298,7 @@ namespace osu.Game.Rulesets.Objects calculatedPath.Add(segmentVertices[0]); else if (segmentVertices.Length > 1) { - List subPath = calculateSubPath(segmentVertices, segmentType, ref optimisedLength); + List subPath = calculateSubPath(segmentVertices, segmentType); // Skip the first vertex if it is the same as the last vertex from the previous segment bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0]; @@ -300,7 +318,7 @@ namespace osu.Game.Rulesets.Objects } } - private static List calculateSubPath(ReadOnlySpan subControlPoints, PathType type, ref double optimisedLength) + private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) { switch (type.Type) { @@ -325,6 +343,9 @@ namespace osu.Game.Rulesets.Objects { List subPath = PathApproximator.CatmullToPiecewiseLinear(subControlPoints); + if (!OptimiseCatmull) + return subPath; + // At draw time, osu!stable optimises paths by only keeping piecewise segments that are 6px apart. // For the most part we don't care about this optimisation, and its additional heuristics are hard to reproduce in every implementation. // From ed5dd5c8cd100f99223a20360e10e52668c64edb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Apr 2024 13:04:34 +0800 Subject: [PATCH 172/581] Bind using local bindables to avoid potentially event pollution --- osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs index 3e2cbe9d60..9499bacade 100644 --- a/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs @@ -22,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Edit private EditorToolButton rotateButton = null!; + private Bindable canRotatePlayfieldOrigin = null!; + private Bindable canRotateSelectionOrigin = null!; + public SelectionRotationHandler RotationHandler { get; init; } = null!; public TransformToolboxGroup() @@ -52,8 +55,11 @@ namespace osu.Game.Rulesets.Osu.Edit base.LoadComplete(); // aggregate two values into canRotate - RotationHandler.CanRotatePlayfieldOrigin.BindValueChanged(_ => updateCanRotateAggregate()); - RotationHandler.CanRotateSelectionOrigin.BindValueChanged(_ => updateCanRotateAggregate()); + canRotatePlayfieldOrigin = RotationHandler.CanRotatePlayfieldOrigin.GetBoundCopy(); + canRotatePlayfieldOrigin.BindValueChanged(_ => updateCanRotateAggregate()); + + canRotateSelectionOrigin = RotationHandler.CanRotateSelectionOrigin.GetBoundCopy(); + canRotateSelectionOrigin.BindValueChanged(_ => updateCanRotateAggregate()); void updateCanRotateAggregate() { From eca242c1c73567433a25e34e075740b35ec7119d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Apr 2024 13:17:16 +0800 Subject: [PATCH 173/581] Change tooltip text slightly --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index da50233920..caf02d1dda 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; selectionCentreButton.Selected.DisabledChanged += isDisabled => { - selectionCentreButton.TooltipText = isDisabled ? "Select more than one circles to perform rotation." : string.Empty; + selectionCentreButton.TooltipText = isDisabled ? "Select more than one objects to perform selection-based rotation." : string.Empty; }; } From 6642702fa94e1819ec13243d87a6626c88e88a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=C3=AAn=20Minh=20H=E1=BB=93?= <32929093+honguyenminh@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:36:44 +0700 Subject: [PATCH 174/581] Update plurals in editor rotate button tooltip Co-authored-by: Joseph Madamba --- osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs index caf02d1dda..88c3d7414b 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; selectionCentreButton.Selected.DisabledChanged += isDisabled => { - selectionCentreButton.TooltipText = isDisabled ? "Select more than one objects to perform selection-based rotation." : string.Empty; + selectionCentreButton.TooltipText = isDisabled ? "Select more than one object to perform selection-based rotation." : string.Empty; }; } From 2a2a372595a7a64dc1b2fe660e6f053e4522cd1e Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 2 Apr 2024 07:45:27 -0300 Subject: [PATCH 175/581] Check if `blueprint` is in `SelectionBlueprints` before changing its depth --- .../Editor/TestSceneObjectMerging.cs | 132 ++++++++++++++++++ .../Compose/Components/BlueprintContainer.cs | 4 +- 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs index 3d35ab79f7..2da4c83a42 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs @@ -231,6 +231,138 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor (pos: circle2.Position, pathType: null))); } + [Test] + public void TestMergeSliderSliderSameStartTime() + { + Slider? slider1 = null; + SliderPath? slider1Path = null; + Slider? slider2 = null; + + AddStep("select two sliders", () => + { + slider1 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider); + slider1Path = new SliderPath(slider1.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(), slider1.Path.ExpectedDistance.Value); + slider2 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider && h.StartTime > slider1.StartTime); + EditorClock.Seek(slider1.StartTime); + EditorBeatmap.SelectedHitObjects.AddRange([slider1, slider2]); + }); + + AddStep("move sliders to the same start time", () => + { + slider2!.StartTime = slider1!.StartTime; + }); + + mergeSelection(); + + AddAssert("slider created", () => + { + if (slider1 is null || slider2 is null || slider1Path is null) + return false; + + var controlPoints1 = slider1Path.ControlPoints; + var controlPoints2 = slider2.Path.ControlPoints; + (Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints1.Count + controlPoints2.Count - 1]; + + for (int i = 0; i < controlPoints1.Count - 1; i++) + { + args[i] = (controlPoints1[i].Position + slider1.Position, controlPoints1[i].Type); + } + + for (int i = 0; i < controlPoints2.Count; i++) + { + args[i + controlPoints1.Count - 1] = (controlPoints2[i].Position + controlPoints1[^1].Position + slider1.Position, controlPoints2[i].Type); + } + + return sliderCreatedFor(args); + }); + + AddAssert("samples exist", sliderSampleExist); + + AddAssert("merged slider matches first slider", () => + { + var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); + return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples) + && mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples) + && mergedSlider.Samples.SequenceEqual(slider1.Samples); + }); + + AddAssert("slider end is at same completion for last slider", () => + { + if (slider1Path is null || slider2 is null) + return false; + + var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); + return Precision.AlmostEquals(mergedSlider.Path.Distance, slider1Path.CalculatedDistance + slider2.Path.Distance); + }); + } + + [Test] + public void TestMergeSliderSliderSameStartAndEndTime() + { + Slider? slider1 = null; + SliderPath? slider1Path = null; + Slider? slider2 = null; + + AddStep("select two sliders", () => + { + slider1 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider); + slider1Path = new SliderPath(slider1.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(), slider1.Path.ExpectedDistance.Value); + slider2 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider && h.StartTime > slider1.StartTime); + EditorClock.Seek(slider1.StartTime); + EditorBeatmap.SelectedHitObjects.AddRange([slider1, slider2]); + }); + + AddStep("move sliders to the same start & end time", () => + { + slider2!.StartTime = slider1!.StartTime; + slider2.Path = slider1.Path; + }); + + mergeSelection(); + + AddAssert("slider created", () => + { + if (slider1 is null || slider2 is null || slider1Path is null) + return false; + + var controlPoints1 = slider1Path.ControlPoints; + var controlPoints2 = slider2.Path.ControlPoints; + (Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints1.Count + controlPoints2.Count - 1]; + + for (int i = 0; i < controlPoints1.Count - 1; i++) + { + args[i] = (controlPoints1[i].Position + slider1.Position, controlPoints1[i].Type); + } + + for (int i = 0; i < controlPoints2.Count; i++) + { + args[i + controlPoints1.Count - 1] = (controlPoints2[i].Position + controlPoints1[^1].Position + slider1.Position, controlPoints2[i].Type); + } + + return sliderCreatedFor(args); + }); + + AddAssert("samples exist", sliderSampleExist); + + AddAssert("merged slider matches first slider", () => + { + var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); + return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples) + && mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples) + && mergedSlider.Samples.SequenceEqual(slider1.Samples); + }); + + AddAssert("slider end is at same completion for last slider", () => + { + if (slider1Path is null || slider2 is null) + return false; + + var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); + return Precision.AlmostEquals(mergedSlider.Path.Distance, slider1Path.CalculatedDistance + slider2.Path.Distance); + }); + } + + private void mergeSelection() { AddStep("merge selection", () => diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 2d6e234e57..c66be90605 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -512,7 +512,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual void OnBlueprintDeselected(SelectionBlueprint blueprint) { - SelectionBlueprints.ChangeChildDepth(blueprint, 0); + if (SelectionBlueprints.Contains(blueprint)) + SelectionBlueprints.ChangeChildDepth(blueprint, 0); + SelectionHandler.HandleDeselected(blueprint); } From 9315aefe41a48501aa6ad222df849d0dd9b3dc89 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 2 Apr 2024 08:48:01 -0300 Subject: [PATCH 176/581] Fix remove additional blank line --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs index 2da4c83a42..76982b05a7 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs @@ -362,7 +362,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor }); } - private void mergeSelection() { AddStep("merge selection", () => From 94cbe1838fff6ce48d01fccea2ba14ac9c5b2067 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Apr 2024 01:50:39 +0800 Subject: [PATCH 177/581] Replace usages of `is null` with `== null` --- .../Editor/TestSceneObjectMerging.cs | 14 +++++++------- .../Editor/TestSceneSliderSplitting.cs | 6 +++--- .../Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/PathControlPoint.cs | 2 +- osu.Game/Rulesets/Objects/SliderPathExtensions.cs | 4 ++-- osu.Game/Rulesets/UI/ModIcon.cs | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs index 76982b05a7..dfe950c01e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider created", () => { - if (circle1 is null || circle2 is null || slider is null) + if (circle1 == null || circle2 == null || slider == null) return false; var controlPoints = slider.Path.ControlPoints; @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider created", () => { - if (slider1 is null || slider2 is null || slider1Path is null) + if (slider1 == null || slider2 == null || slider1Path == null) return false; var controlPoints1 = slider1Path.ControlPoints; @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider end is at same completion for last slider", () => { - if (slider1Path is null || slider2 is null) + if (slider1Path == null || slider2 == null) return false; var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); @@ -256,7 +256,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider created", () => { - if (slider1 is null || slider2 is null || slider1Path is null) + if (slider1 == null || slider2 == null || slider1Path == null) return false; var controlPoints1 = slider1Path.ControlPoints; @@ -288,7 +288,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider end is at same completion for last slider", () => { - if (slider1Path is null || slider2 is null) + if (slider1Path == null || slider2 == null) return false; var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); @@ -322,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider created", () => { - if (slider1 is null || slider2 is null || slider1Path is null) + if (slider1 == null || slider2 == null || slider1Path == null) return false; var controlPoints1 = slider1Path.ControlPoints; @@ -354,7 +354,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider end is at same completion for last slider", () => { - if (slider1Path is null || slider2 is null) + if (slider1Path == null || slider2 == null) return false; var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First(); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index 6c7733e68a..d68cbe6265 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("add hitsounds", () => { - if (slider is null) return; + if (slider == null) return; sample = new HitSampleInfo("hitwhistle", HitSampleInfo.BANK_SOFT, volume: 70); slider.Samples.Add(sample.With()); @@ -228,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"move mouse to control point {index}", () => { - if (slider is null || visualiser is null) return; + if (slider == null || visualiser == null) return; Vector2 position = slider.Path.ControlPoints[index].Position + slider.Position; InputManager.MoveMouseTo(visualiser.Pieces[0].Parent!.ToScreenSpace(position)); @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"click context menu item \"{contextMenuText}\"", () => { - if (visualiser is null) return; + if (visualiser == null) return; MenuItem? item = visualiser.ContextMenuItems?.FirstOrDefault(menuItem => menuItem.Text.Value == contextMenuText); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 4d2b980c23..2da462caf4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -311,7 +311,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders foreach (var splitPoint in controlPointsToSplitAt) { - if (splitPoint == controlPoints[0] || splitPoint == controlPoints[^1] || splitPoint.Type is null) + if (splitPoint == controlPoints[0] || splitPoint == controlPoints[^1] || splitPoint.Type == null) continue; // Split off the section of slider before this control point so the remaining control points to split are in the latter part of the slider. diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 53c24dc828..3325cfe407 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -781,7 +781,7 @@ namespace osu.Game.Overlays.Mods /// > public bool OnPressed(KeyBindingPressEvent e) { - if (e.Repeat || e.Action != PlatformAction.SelectAll || SelectAllModsButton is null) + if (e.Repeat || e.Action != PlatformAction.SelectAll || SelectAllModsButton == null) return false; SelectAllModsButton.TriggerClick(); diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 283a59b7ed..66b3033f90 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Objects.Legacy // Edge-case rules (to match stable). if (type == PathType.PERFECT_CURVE) { - int endPointLength = endPoint is null ? 0 : 1; + int endPointLength = endPoint == null ? 0 : 1; if (vertices.Length + endPointLength != 3) type = PathType.BEZIER; diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index 1f8e63b269..32245e9080 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Objects public bool Equals(PathControlPoint other) => Position == other?.Position && Type == other.Type; - public override string ToString() => type is null + public override string ToString() => type == null ? $"Position={Position}" : $"Position={Position}, Type={type}"; } diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 29b34ae4f0..c03d3646da 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Objects { var controlPoints = sliderPath.ControlPoints; - var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.LINEAR && p.Type is null).ToList(); + var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.LINEAR && p.Type == null).ToList(); // Inherited points after a linear point, as well as the first control point if it inherited, // should be treated as linear points, so their types are temporarily changed to linear. @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects inheritedLinearPoints.ForEach(p => p.Type = null); // Recalculate middle perfect curve control points at the end of the slider path. - if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECT_CURVE && controlPoints[^2].Type is null && segmentEnds.Any()) + if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECT_CURVE && controlPoints[^2].Type == null && segmentEnds.Any()) { double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0; double lastSegmentEnd = segmentEnds[^1]; diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index d1776c5c0b..5d9fafd60c 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.UI modAcronym.Text = value.Acronym; modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question; - if (value.Icon is null) + if (value.Icon == null) { modIcon.FadeOut(); modAcronym.FadeIn(); From b5adcf2e0e8f94056af1f868d3fcc1baf37f08c0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 3 Apr 2024 17:32:02 +0900 Subject: [PATCH 178/581] Fix SpectatorClient holding references to Player --- osu.Game/Online/Spectator/SpectatorClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 07ee9115d6..fb7a3d13ca 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -248,6 +248,9 @@ namespace osu.Game.Online.Spectator isPlaying = false; currentBeatmap = null; + currentScore = null; + currentScoreProcessor = null; + currentScoreToken = null; if (state.HasPassed) currentState.State = SpectatedUserState.Passed; From ce68f6adb76c909af038284621d410e720beae61 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 3 Apr 2024 17:46:26 +0900 Subject: [PATCH 179/581] Fix SkinEditor binding event to external bindable --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index d3af928907..ac9649bcba 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -52,6 +52,7 @@ namespace osu.Game.Overlays.SkinEditor private OsuTextFlowContainer headerText = null!; private Bindable currentSkin = null!; + private Bindable clipboardContent = null!; [Resolved] private OsuGame? game { get; set; } @@ -243,7 +244,8 @@ namespace osu.Game.Overlays.SkinEditor canCopy.Value = canCut.Value = SelectedComponents.Any(); }, true); - clipboard.Content.BindValueChanged(content => canPaste.Value = !string.IsNullOrEmpty(content.NewValue), true); + clipboardContent = clipboard.Content.GetBoundCopy(); + clipboardContent.BindValueChanged(content => canPaste.Value = !string.IsNullOrEmpty(content.NewValue), true); Show(); From 05fe8968d88f763621afe3042d665afaa8934331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Apr 2024 11:39:12 +0200 Subject: [PATCH 180/581] Only interact with clipboard via bound copy --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index ac9649bcba..619eac8f4a 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -66,9 +66,6 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private RealmAccess realm { get; set; } = null!; - [Resolved] - private EditorClipboard clipboard { get; set; } = null!; - [Resolved] private SkinEditorOverlay? skinEditorOverlay { get; set; } @@ -114,7 +111,7 @@ namespace osu.Game.Overlays.SkinEditor } [BackgroundDependencyLoader] - private void load() + private void load(EditorClipboard clipboard) { RelativeSizeAxes = Axes.Both; @@ -225,6 +222,8 @@ namespace osu.Game.Overlays.SkinEditor } } }; + + clipboardContent = clipboard.Content.GetBoundCopy(); } protected override void LoadComplete() @@ -244,7 +243,6 @@ namespace osu.Game.Overlays.SkinEditor canCopy.Value = canCut.Value = SelectedComponents.Any(); }, true); - clipboardContent = clipboard.Content.GetBoundCopy(); clipboardContent.BindValueChanged(content => canPaste.Value = !string.IsNullOrEmpty(content.NewValue), true); Show(); @@ -497,7 +495,7 @@ namespace osu.Game.Overlays.SkinEditor protected void Copy() { - clipboard.Content.Value = JsonConvert.SerializeObject(SelectedComponents.Cast().Select(s => s.CreateSerialisedInfo()).ToArray()); + clipboardContent.Value = JsonConvert.SerializeObject(SelectedComponents.Cast().Select(s => s.CreateSerialisedInfo()).ToArray()); } protected void Clone() @@ -517,7 +515,7 @@ namespace osu.Game.Overlays.SkinEditor changeHandler?.BeginChange(); - var drawableInfo = JsonConvert.DeserializeObject(clipboard.Content.Value); + var drawableInfo = JsonConvert.DeserializeObject(clipboardContent.Value); if (drawableInfo == null) return; From 16276dfcd6ce5c35d50fa0c63877ff293c2bbdd0 Mon Sep 17 00:00:00 2001 From: Mafalda Fernandes Date: Mon, 1 Apr 2024 19:21:05 +0100 Subject: [PATCH 181/581] Fix #27105: Mod search box doesnt track external focus changes In the Mod selection area, the search bar's focus could be changed by pressing TAB. However, when clicking outside of the search bar, the focus would be killed but two TABs were required to get the focus back on the search bar. This happened because the action of clicking in an empty area would trigger the search bar to change its appearence, but not its internal state. In my solution, I made the OnClick function aware of the search bar's state, so it would not only change its appearance, but also its state. Now, after clicking in an empty area, there is only needed one TAB to select the search box again, as expected. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 3325cfe407..5ca26c739e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -420,7 +420,7 @@ namespace osu.Game.Overlays.Mods yield return new ColumnDimContainer(new ModPresetColumn { Margin = new MarginPadding { Right = 10 } - }); + }, this); } yield return createModColumnContent(ModType.DifficultyReduction); @@ -438,7 +438,7 @@ namespace osu.Game.Overlays.Mods column.Margin = new MarginPadding { Right = 10 }; }); - return new ColumnDimContainer(column); + return new ColumnDimContainer(column, this); } private void createLocalMods() @@ -899,13 +899,17 @@ namespace osu.Game.Overlays.Mods [Resolved] private OsuColour colours { get; set; } = null!; - public ColumnDimContainer(ModSelectColumn column) + private ModSelectOverlay modSelectOverlayInstance; + + public ColumnDimContainer(ModSelectColumn column, ModSelectOverlay modSelectOverlay) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; Child = Column = column; column.Active.BindTo(Active); + + this.modSelectOverlayInstance = modSelectOverlay; } [BackgroundDependencyLoader] @@ -953,7 +957,7 @@ namespace osu.Game.Overlays.Mods RequestScroll?.Invoke(this); // Killing focus is done here because it's the only feasible place on ModSelectOverlay you can click on without triggering any action. - Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null)); + modSelectOverlayInstance.setTextBoxFocus(false); return true; } From 524a5815bc3bf0842e6161efe7719abea550713f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Apr 2024 16:11:23 +0200 Subject: [PATCH 182/581] Add test coverage --- .../Gameplay/TestSceneStoryboardWithIntro.cs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs new file mode 100644 index 0000000000..502a0de616 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs @@ -0,0 +1,89 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Play; +using osu.Game.Storyboards; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public partial class TestSceneStoryboardWithIntro : PlayerTestScene + { + protected override bool HasCustomSteps => true; + protected override bool AllowFail => true; + + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HitCircle { StartTime = firstObjectStartTime }); + return beatmap; + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) + { + return base.CreateWorkingBeatmap(beatmap, createStoryboard(storyboardStartTime)); + } + + private Storyboard createStoryboard(double startTime) + { + var storyboard = new Storyboard(); + var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); + sprite.TimelineGroup.Alpha.Add(Easing.None, startTime, 0, 0, 1); + storyboard.GetLayer("Background").Add(sprite); + return storyboard; + } + + private double firstObjectStartTime; + private double storyboardStartTime; + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true)); + AddStep("set dim level to 0", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0)); + AddStep("reset first hitobject time", () => firstObjectStartTime = 0); + AddStep("reset storyboard start time", () => storyboardStartTime = 0); + } + + [TestCase(-5000, 0)] + [TestCase(-5000, 30000)] + public void TestStoryboardSingleSkip(double storyboardStart, double firstObject) + { + AddStep($"set storyboard start time to {storyboardStart}", () => storyboardStartTime = storyboardStart); + AddStep($"set first object start time to {firstObject}", () => firstObjectStartTime = firstObject); + CreateTest(); + + AddStep("skip", () => InputManager.Key(osuTK.Input.Key.Space)); + AddAssert("skip performed", () => Player.ChildrenOfType().Any(s => s.SkipCount == 1)); + AddUntilStep("gameplay clock advanced", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(firstObject - 2000)); + } + + [Test] + public void TestStoryboardDoubleSkip() + { + AddStep("set storyboard start time to -11000", () => storyboardStartTime = -11000); + AddStep("set first object start time to 11000", () => firstObjectStartTime = 11000); + CreateTest(); + + AddStep("skip", () => InputManager.Key(osuTK.Input.Key.Space)); + AddAssert("skip performed", () => Player.ChildrenOfType().Any(s => s.SkipCount == 1)); + AddUntilStep("gameplay clock advanced", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0)); + + AddStep("skip", () => InputManager.Key(osuTK.Input.Key.Space)); + AddAssert("skip performed", () => Player.ChildrenOfType().Any(s => s.SkipCount == 2)); + AddUntilStep("gameplay clock advanced", () => Player.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(9000)); + } + } +} From 9d54f1a09270b9d9758a2df837c958476e6ec16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Apr 2024 16:12:20 +0200 Subject: [PATCH 183/581] Fix some maps requiring multiple intro skips that weren't there on stable Closes https://github.com/ppy/osu/issues/25633. The reason why that particular beatmap did not have a double skip on stable is here: https://github.com/peppy/osu-stable-reference/blob/e53980dd76857ee899f66ce519ba1597e7874f28/osu!/GameModes/Play/Player.cs#L1761-L1770 The particular place of interest is the `leadInTime < 10000` check. If `leadInTime < 10000`, then `leadIn == leadInTime`, and it turns out that `AudioEngine.Time` will always be more than or equal to `leadIn`, because it's also the gameplay start time: https://github.com/peppy/osu-stable-reference/blob/e53980dd76857ee899f66ce519ba1597e7874f28/osu!/GameModes/Play/Player.cs#L2765 This essentially means that if the `leadInTime` is less than 10000, that particular check is just dead. So a double skip can only occur if the gameplay starts at time -10000 or earlier due to the storyboard. --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 93bdcb1cab..b2f0ae5561 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play double skipTarget = skipTargetTime - MINIMUM_SKIP_TIME; - if (GameplayClock.CurrentTime < 0 && skipTarget > 6000) + if (StartTime < -10000 && GameplayClock.CurrentTime < 0 && skipTarget > 6000) // double skip exception for storyboards with very long intros skipTarget = 0; From 8e00368f7cf7a4be473d18316ca4fe56080918eb Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 3 Apr 2024 11:30:14 -0300 Subject: [PATCH 184/581] Add custom message in the case of a invalid beatmap_hash --- osu.Game/Screens/Play/SubmittingPlayer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 62226c46dd..ce8260a52f 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -152,6 +152,10 @@ namespace osu.Game.Screens.Play Logger.Log($"Please ensure that you are using the latest version of the official game releases.\n\n{whatWillHappen}", level: LogLevel.Important); break; + case @"invalid beatmap hash": + Logger.Log($"A new version of this beatmapset is available please update. \n\n{whatWillHappen}", level: LogLevel.Important); + break; + case @"expired token": Logger.Log($"Your system clock is set incorrectly. Please check your system time, date and timezone.\n\n{whatWillHappen}", level: LogLevel.Important); break; From 9e92ebaa437c8adbbd2a406e8a493f8443d9d05d Mon Sep 17 00:00:00 2001 From: Arthur Araujo <90941580+64ArthurAraujo@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:15:22 -0300 Subject: [PATCH 185/581] Make message more general Co-authored-by: Walavouchey <36758269+Walavouchey@users.noreply.github.com> --- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index ce8260a52f..8ccfd039ec 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -153,7 +153,7 @@ namespace osu.Game.Screens.Play break; case @"invalid beatmap hash": - Logger.Log($"A new version of this beatmapset is available please update. \n\n{whatWillHappen}", level: LogLevel.Important); + Logger.Log($"This beatmap does not match the online version. Please update or redownload it.\n\n{whatWillHappen}", level: LogLevel.Important); break; case @"expired token": From 9521c1e3e42e4c73fd94f840ce41585ff0552570 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Apr 2024 14:31:40 +0800 Subject: [PATCH 186/581] Update hit error metre to use new icons - [ ] Depends on https://github.com/ppy/osu-resources/pull/317. --- osu.Game/Graphics/OsuIcon.cs | 8 ++++++++ .../Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index 3cd10b1315..32e780f11c 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -175,6 +175,8 @@ namespace osu.Game.Graphics public static IconUsage EditorSelect => get(OsuIconMapping.EditorSelect); public static IconUsage EditorSound => get(OsuIconMapping.EditorSound); public static IconUsage EditorWhistle => get(OsuIconMapping.EditorWhistle); + public static IconUsage Tortoise => get(OsuIconMapping.Tortoise); + public static IconUsage Hare => get(OsuIconMapping.Hare); private static IconUsage get(OsuIconMapping glyph) => new IconUsage((char)glyph, FONT_NAME); @@ -380,6 +382,12 @@ namespace osu.Game.Graphics [Description(@"Editor/whistle")] EditorWhistle, + + [Description(@"tortoise")] + Tortoise, + + [Description(@"hare")] + Hare, } public class OsuIconStore : ITextureStore, ITexturedGlyphLookupStore diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 443863fb2f..a71a46ec2a 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -303,13 +303,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters labelEarly.Child = new SpriteIcon { Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.ShippingFast, + Icon = OsuIcon.Hare }; labelLate.Child = new SpriteIcon { Size = new Vector2(icon_size), - Icon = FontAwesome.Solid.Bicycle, + Icon = OsuIcon.Tortoise }; break; From cd474de61f963c924d42291a8872ae7c6ffa0988 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 15:55:05 +0900 Subject: [PATCH 187/581] Update osu.Framework package --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4901f30d8a..2d7a9d2652 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 6b63bfa1e2..b2e3fc0779 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 10d1308a0a1b69a463be814639425b4967191689 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Apr 2024 15:05:59 +0800 Subject: [PATCH 188/581] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0e091dbd37..feaf47b809 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From fb8fb4f34e2e7ef325248aaf264f8035a05795fc Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 16:43:26 +0900 Subject: [PATCH 189/581] Disable Discord URI registration on macOS for now --- osu.Desktop/DiscordRichPresence.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 6e8554d617..7553924d1b 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -6,6 +6,7 @@ using System.Text; using DiscordRPC; using DiscordRPC.Message; using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; @@ -78,9 +79,13 @@ namespace osu.Desktop client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Message} ({e.Code})", LoggingTarget.Network, LogLevel.Error); // A URI scheme is required to support game invitations, as well as informing Discord of the game executable path to support launching the game when a user clicks on join/spectate. - client.RegisterUriScheme(); - client.Subscribe(EventType.Join); - client.OnJoin += onJoin; + // The library doesn't properly support URI registration when ran from an app bundle on macOS. + if (!RuntimeInfo.IsApple) + { + client.RegisterUriScheme(); + client.Subscribe(EventType.Join); + client.OnJoin += onJoin; + } config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); From 7b92c725b1e4004d1e082989522906335efe4859 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 5 Apr 2024 14:45:49 +0900 Subject: [PATCH 190/581] Revert "Merge pull request #27454 from EVAST9919/sb-lifetime-improvements" This reverts commit 0881e7c8c1f003416cc37507ce6448f74e9d1a7f, reversing changes made to 29a37e35852649c2f88b6c917b2921950c9e2dd9. --- osu.Game/Storyboards/CommandTimelineGroup.cs | 41 +++++++++++++- osu.Game/Storyboards/StoryboardSprite.cs | 58 +------------------- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index c899cf77d3..0b96db6861 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.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 System; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics; @@ -45,10 +46,32 @@ namespace osu.Game.Storyboards } [JsonIgnore] - public double CommandsStartTime => timelines.Min(static t => t.StartTime); + public double CommandsStartTime + { + get + { + double min = double.MaxValue; + + for (int i = 0; i < timelines.Length; i++) + min = Math.Min(min, timelines[i].StartTime); + + return min; + } + } [JsonIgnore] - public double CommandsEndTime => timelines.Max(static t => t.EndTime); + public double CommandsEndTime + { + get + { + double max = double.MinValue; + + for (int i = 0; i < timelines.Length; i++) + max = Math.Max(max, timelines[i].EndTime); + + return max; + } + } [JsonIgnore] public double CommandsDuration => CommandsEndTime - CommandsStartTime; @@ -60,7 +83,19 @@ namespace osu.Game.Storyboards public virtual double EndTime => CommandsEndTime; [JsonIgnore] - public bool HasCommands => timelines.Any(static t => t.HasCommands); + public bool HasCommands + { + get + { + for (int i = 0; i < timelines.Length; i++) + { + if (timelines[i].HasCommands) + return true; + } + + return false; + } + } public virtual IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) { diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 4992ae128d..982185d51b 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -85,23 +85,12 @@ namespace osu.Game.Storyboards { get { - double latestEndTime = double.MaxValue; - - // Ignore the whole setup if there are loops. In theory they can be handled here too, however the logic will be overly complex. - if (loops.Count == 0) - { - // Take the minimum time of all the potential "death" reasons. - latestEndTime = calculateOptimisedEndTime(TimelineGroup); - } - - // If the logic above fails to find anything or discarded by the fact that there are loops present, latestEndTime will be double.MaxValue - // and thus conservativeEndTime will be used. - double conservativeEndTime = TimelineGroup.EndTime; + double latestEndTime = TimelineGroup.EndTime; foreach (var l in loops) - conservativeEndTime = Math.Max(conservativeEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); + latestEndTime = Math.Max(latestEndTime, l.StartTime + l.CommandsDuration * l.TotalIterations); - return Math.Min(latestEndTime, conservativeEndTime); + return latestEndTime; } } @@ -205,47 +194,6 @@ namespace osu.Game.Storyboards return commands; } - private static double calculateOptimisedEndTime(CommandTimelineGroup timelineGroup) - { - // Here we are starting from maximum value and trying to minimise the end time on each step. - // There are few solid guesses we can make using which sprite's end time can be minimised: alpha = 0, scale = 0, colour.a = 0. - double[] deathTimes = - { - double.MaxValue, // alpha - double.MaxValue, // colour alpha - double.MaxValue, // scale - double.MaxValue, // scale x - double.MaxValue, // scale y - }; - - // The loops below are following the same pattern. - // We could be using TimelineGroup.EndValue here, however it's possible to have multiple commands with 0 value in a row - // so we are saving the earliest of them. - foreach (var alphaCommand in timelineGroup.Alpha.Commands) - { - if (alphaCommand.EndValue == 0) - // commands are ordered by the start time, however end time may vary. Save the earliest. - deathTimes[0] = Math.Min(alphaCommand.EndTime, deathTimes[0]); - else - // If value isn't 0 (sprite becomes visible again), revert the saved state. - deathTimes[0] = double.MaxValue; - } - - foreach (var colourCommand in timelineGroup.Colour.Commands) - deathTimes[1] = colourCommand.EndValue.A == 0 ? Math.Min(colourCommand.EndTime, deathTimes[1]) : double.MaxValue; - - foreach (var scaleCommand in timelineGroup.Scale.Commands) - deathTimes[2] = scaleCommand.EndValue == 0 ? Math.Min(scaleCommand.EndTime, deathTimes[2]) : double.MaxValue; - - foreach (var scaleCommand in timelineGroup.VectorScale.Commands) - { - deathTimes[3] = scaleCommand.EndValue.X == 0 ? Math.Min(scaleCommand.EndTime, deathTimes[3]) : double.MaxValue; - deathTimes[4] = scaleCommand.EndValue.Y == 0 ? Math.Min(scaleCommand.EndTime, deathTimes[4]) : double.MaxValue; - } - - return deathTimes.Min(); - } - public override string ToString() => $"{Path}, {Origin}, {InitialPosition}"; From fefcd17db90150b8cd18079270a47cf173f96f47 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 8 Apr 2024 22:00:05 +0900 Subject: [PATCH 191/581] Fix gameplay PP counter not matching results screen --- .../Difficulty/DifficultyCalculator.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 00c90bd317..5973a83fd3 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -108,9 +108,18 @@ namespace osu.Game.Rulesets.Difficulty var skills = CreateSkills(Beatmap, playableMods, clockRate); var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap); + // There is a one-to-many relationship between the hitobjects in the beatmap and the "difficulty hitobjects". + // Each iteration of the loop bellow will add at most one hitobject to the progressive beatmap, + // representing the most-parenting hitobject - the hitobject from the original beatmap. + Dictionary hitObjectParentLinks = + createHitObjectParentLinks(Beatmap) + .ToDictionary(k => k.obj, k => k.mostParentingObject); + foreach (var hitObject in getDifficultyHitObjects()) { - progressiveBeatmap.HitObjects.Add(hitObject.BaseObject); + HitObject parent = hitObjectParentLinks[hitObject.BaseObject]; + if (progressiveBeatmap.HitObjects.Count == 0 || parent != progressiveBeatmap.HitObjects[^1]) + progressiveBeatmap.HitObjects.Add(parent); foreach (var skill in skills) { @@ -122,6 +131,23 @@ namespace osu.Game.Rulesets.Difficulty } return attribs; + + static IEnumerable<(HitObject obj, HitObject mostParentingObject)> createHitObjectParentLinks(IBeatmap beatmap) + { + foreach (var link in createNestedLinks(beatmap.HitObjects, null)) + yield return link; + + static IEnumerable<(HitObject obj, HitObject mostParentingObject)> createNestedLinks(IReadOnlyList objects, [CanBeNull] HitObject parent) + { + foreach (var o in objects) + { + yield return (o, parent ?? o); + + foreach (var n in createNestedLinks(o.NestedHitObjects, parent ?? o)) + yield return n; + } + } + } } /// From 7d8fe5117834aeb296cd6be28a89effa2bb9083f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 8 Apr 2024 23:25:45 +0900 Subject: [PATCH 192/581] Fix possible crash due to race in DiscordRichPresence --- osu.Desktop/DiscordRichPresence.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 7553924d1b..ed9d4ca2d2 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -36,8 +36,6 @@ namespace osu.Desktop [Resolved] private IBindable ruleset { get; set; } = null!; - private IBindable user = null!; - [Resolved] private IAPIProvider api { get; set; } = null!; @@ -50,9 +48,11 @@ namespace osu.Desktop [Resolved] private MultiplayerClient multiplayerClient { get; set; } = null!; + [Resolved] + private OsuConfigManager config { get; set; } = null!; + private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); - private readonly Bindable privacyMode = new Bindable(); private readonly RichPresence presence = new RichPresence @@ -65,8 +65,10 @@ namespace osu.Desktop }, }; + private IBindable? user; + [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load() { client = new DiscordRpcClient(client_id) { @@ -87,6 +89,13 @@ namespace osu.Desktop client.OnJoin += onJoin; } + client.Initialize(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); user = api.LocalUser.GetBoundCopy(); @@ -104,8 +113,6 @@ namespace osu.Desktop activity.BindValueChanged(_ => schedulePresenceUpdate()); privacyMode.BindValueChanged(_ => schedulePresenceUpdate()); multiplayerClient.RoomUpdated += onRoomUpdated; - - client.Initialize(); } private void onReady(object _, ReadyMessage __) @@ -146,6 +153,9 @@ namespace osu.Desktop private void updatePresence(bool hideIdentifiableInformation) { + if (user == null) + return; + // user activity if (activity.Value != null) { From 14c26926f328c646ee3d7d4ec28f6199976238f8 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 9 Apr 2024 09:55:50 +0200 Subject: [PATCH 193/581] Upgrade to SDL3 --- osu.Desktop/OsuGameDesktop.cs | 11 ++++++----- osu.Desktop/Program.cs | 25 ++++++++++++++----------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 2b232db274..e8783c997a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,7 +22,7 @@ using osu.Game.IPC; using osu.Game.Online.Multiplayer; using osu.Game.Performance; using osu.Game.Utils; -using SDL2; +using SDL; namespace osu.Desktop { @@ -161,7 +161,7 @@ namespace osu.Desktop host.Window.Title = Name; } - protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo(); + protected override BatteryInfo CreateBatteryInfo() => new SDL3BatteryInfo(); protected override void Dispose(bool isDisposing) { @@ -170,13 +170,14 @@ namespace osu.Desktop archiveImportIPCChannel?.Dispose(); } - private class SDL2BatteryInfo : BatteryInfo + private unsafe class SDL3BatteryInfo : BatteryInfo { public override double? ChargeLevel { get { - SDL.SDL_GetPowerInfo(out _, out int percentage); + int percentage; + SDL3.SDL_GetPowerInfo(null, &percentage); if (percentage == -1) return null; @@ -185,7 +186,7 @@ namespace osu.Desktop } } - public override bool OnBattery => SDL.SDL_GetPowerInfo(out _, out _) == SDL.SDL_PowerState.SDL_POWERSTATE_ON_BATTERY; + public override bool OnBattery => SDL3.SDL_GetPowerInfo(null, null) == SDL_PowerState.SDL_POWERSTATE_ON_BATTERY; } } } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 2d7ec5aa5f..29b05a402f 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -13,7 +13,7 @@ using osu.Framework.Platform; using osu.Game; using osu.Game.IPC; using osu.Game.Tournament; -using SDL2; +using SDL; using Squirrel; namespace osu.Desktop @@ -52,16 +52,19 @@ namespace osu.Desktop // See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/ if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2)) { - // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider - // disabling it ourselves. - // We could also better detect compatibility mode if required: - // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 - SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, - "Your operating system is too old to run osu!", - "This version of osu! requires at least Windows 8.1 to run.\n" - + "Please upgrade your operating system or consider using an older version of osu!.\n\n" - + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero); - return; + unsafe + { + // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider + // disabling it ourselves. + // We could also better detect compatibility mode if required: + // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 + SDL3.SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, + "Your operating system is too old to run osu!"u8, + "This version of osu! requires at least Windows 8.1 to run.\n"u8 + + "Please upgrade your operating system or consider using an older version of osu!.\n\n"u8 + + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!"u8, null); + return; + } } setupSquirrel(); From 6cb5bffdfc07cbd17abe6bd4ddf01148d975d928 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Apr 2024 13:03:37 +0800 Subject: [PATCH 194/581] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 61ad2a4f5a..3ea756a8b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From 3e8ddbd2a9d49173139dd06f709693d0999f456e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 11 Apr 2024 11:33:16 +0800 Subject: [PATCH 195/581] Add new entries to dotsettings (Rider 2024.1) --- osu.sln.DotSettings | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 452f90ecea..dd71744bf0 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -774,9 +774,19 @@ See the LICENCE file in the repository root for full licence text. <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"><ElementKinds><Kind Name="TYPE_PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local functions"><ElementKinds><Kind Name="LOCAL_FUNCTION" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"><ElementKinds><Kind Name="ENUM_MEMBER" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy> <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"><ElementKinds><Kind Name="LOCAL_CONSTANT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> @@ -841,6 +851,7 @@ See the LICENCE file in the repository root for full licence text. True True True + True TestFolder True True From f5555b9fa4f82564b2fa17c41aa0af91ab9e177b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Apr 2024 16:53:04 +0900 Subject: [PATCH 196/581] Also add potentially missed intermediate parents --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5973a83fd3..084ead30c9 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -117,9 +117,11 @@ namespace osu.Game.Rulesets.Difficulty foreach (var hitObject in getDifficultyHitObjects()) { + // Add hitobjects between the original and progressive beatmap until the current hitobject's parent appears in the progressive beatmap. + // This covers cases where hitobjects aren't assigned "difficulty" representations because they don't meaningfully contribute to the calculations. HitObject parent = hitObjectParentLinks[hitObject.BaseObject]; - if (progressiveBeatmap.HitObjects.Count == 0 || parent != progressiveBeatmap.HitObjects[^1]) - progressiveBeatmap.HitObjects.Add(parent); + while (progressiveBeatmap.HitObjects.LastOrDefault() != parent) + progressiveBeatmap.HitObjects.Add(Beatmap.HitObjects[progressiveBeatmap.HitObjects.Count]); foreach (var skill in skills) { From bf63ba3f82a367033d21f001c45d0951a36c38c4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Apr 2024 17:56:34 +0900 Subject: [PATCH 197/581] Add test --- .../TestSceneTimedDifficultyCalculation.cs | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs new file mode 100644 index 0000000000..b0b06ce292 --- /dev/null +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -0,0 +1,191 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class TestSceneTimedDifficultyCalculation + { + [Test] + public void TestAttributesGeneratedForAllNonSkippedObjects() + { + var beatmap = new Beatmap + { + HitObjects = + { + new TestHitObject(), + new TestHitObject { Nested = 1 }, + new TestHitObject(), + } + }; + + List attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed(); + + Assert.That(attribs.Count, Is.EqualTo(4)); + assertEquals(attribs[0], beatmap.HitObjects[0]); + assertEquals(attribs[1], beatmap.HitObjects[0], beatmap.HitObjects[1]); + assertEquals(attribs[2], beatmap.HitObjects[0], beatmap.HitObjects[1]); // From the nested object. + assertEquals(attribs[3], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]); + } + + [Test] + public void TestAttributesNotGeneratedForSkippedObjects() + { + var beatmap = new Beatmap + { + HitObjects = + { + // The first object is usually skipped in all implementations + new TestHitObject { Skip = true }, + // An intermediate skipped object. + new TestHitObject { Skip = true }, + new TestHitObject(), + } + }; + + List attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed(); + + Assert.That(attribs.Count, Is.EqualTo(1)); + assertEquals(attribs[0], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]); + } + + [Test] + public void TestNestedObjectOnlyAddsParentOnce() + { + var beatmap = new Beatmap + { + HitObjects = + { + new TestHitObject { Skip = true, Nested = 2 }, + } + }; + + List attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed(); + + Assert.That(attribs.Count, Is.EqualTo(2)); + assertEquals(attribs[0], beatmap.HitObjects[0]); + assertEquals(attribs[1], beatmap.HitObjects[0]); + } + + private void assertEquals(TimedDifficultyAttributes attribs, params HitObject[] expected) + { + Assert.That(((TestDifficultyAttributes)attribs.Attributes).Objects, Is.EquivalentTo(expected)); + } + + private class TestHitObject : HitObject + { + /// + /// Whether to skip generating a difficulty representation for this object. + /// + public bool Skip { get; set; } + + /// + /// Whether to generate nested difficulty representations for this object, and if so, how many. + /// + public int Nested { get; set; } + + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) + { + for (int i = 0; i < Nested; i++) + AddNested(new TestHitObject()); + } + } + + private class TestRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => Enumerable.Empty(); + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => throw new NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new PassThroughBeatmapConverter(beatmap); + + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TestDifficultyCalculator(beatmap); + + public override string Description => string.Empty; + public override string ShortName => string.Empty; + + private class PassThroughBeatmapConverter : IBeatmapConverter + { + public event Action>? ObjectConverted + { + add { } + remove { } + } + + public IBeatmap Beatmap { get; } + + public PassThroughBeatmapConverter(IBeatmap beatmap) + { + Beatmap = beatmap; + } + + public bool CanConvert() => true; + + public IBeatmap Convert(CancellationToken cancellationToken = default) => Beatmap; + } + } + + private class TestDifficultyCalculator : DifficultyCalculator + { + public TestDifficultyCalculator(IWorkingBeatmap beatmap) + : base(new TestRuleset().RulesetInfo, beatmap) + { + } + + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) + => new TestDifficultyAttributes { Objects = beatmap.HitObjects.ToArray() }; + + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) + { + List objects = new List(); + + foreach (var obj in beatmap.HitObjects.OfType()) + { + if (!obj.Skip) + objects.Add(new DifficultyHitObject(obj, obj, clockRate, objects, objects.Count)); + + foreach (var nested in obj.NestedHitObjects) + objects.Add(new DifficultyHitObject(nested, nested, clockRate, objects, objects.Count)); + } + + return objects; + } + + protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new PassThroughSkill(mods) }; + + private class PassThroughSkill : Skill + { + public PassThroughSkill(Mod[] mods) + : base(mods) + { + } + + public override void Process(DifficultyHitObject current) + { + } + + public override double DifficultyValue() => 1; + } + } + + private class TestDifficultyAttributes : DifficultyAttributes + { + public HitObject[] Objects = Array.Empty(); + } + } +} From 19cc847be6dd9674ac374d3bfef7e9a295f08e9f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 11 Apr 2024 23:40:40 +0900 Subject: [PATCH 198/581] Implement a less failure-prone method --- .../TestSceneTimedDifficultyCalculation.cs | 33 ++++++++++++---- .../Difficulty/DifficultyCalculator.cs | 39 +++++-------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs index b0b06ce292..4c32d3bf1c 100644 --- a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -28,9 +28,13 @@ namespace osu.Game.Tests.NonVisual { HitObjects = { - new TestHitObject(), - new TestHitObject { Nested = 1 }, - new TestHitObject(), + new TestHitObject { StartTime = 1 }, + new TestHitObject + { + StartTime = 2, + Nested = 1 + }, + new TestHitObject { StartTime = 3 }, } }; @@ -51,10 +55,18 @@ namespace osu.Game.Tests.NonVisual HitObjects = { // The first object is usually skipped in all implementations - new TestHitObject { Skip = true }, + new TestHitObject + { + StartTime = 1, + Skip = true + }, // An intermediate skipped object. - new TestHitObject { Skip = true }, - new TestHitObject(), + new TestHitObject + { + StartTime = 2, + Skip = true + }, + new TestHitObject { StartTime = 3 }, } }; @@ -71,7 +83,12 @@ namespace osu.Game.Tests.NonVisual { HitObjects = { - new TestHitObject { Skip = true, Nested = 2 }, + new TestHitObject + { + StartTime = 1, + Skip = true, + Nested = 2 + }, } }; @@ -102,7 +119,7 @@ namespace osu.Game.Tests.NonVisual protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { for (int i = 0; i < Nested; i++) - AddNested(new TestHitObject()); + AddNested(new TestHitObject { StartTime = StartTime + 0.1 * i }); } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 084ead30c9..5d608deae2 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -108,20 +108,18 @@ namespace osu.Game.Rulesets.Difficulty var skills = CreateSkills(Beatmap, playableMods, clockRate); var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap); - // There is a one-to-many relationship between the hitobjects in the beatmap and the "difficulty hitobjects". - // Each iteration of the loop bellow will add at most one hitobject to the progressive beatmap, - // representing the most-parenting hitobject - the hitobject from the original beatmap. - Dictionary hitObjectParentLinks = - createHitObjectParentLinks(Beatmap) - .ToDictionary(k => k.obj, k => k.mostParentingObject); - foreach (var hitObject in getDifficultyHitObjects()) { - // Add hitobjects between the original and progressive beatmap until the current hitobject's parent appears in the progressive beatmap. - // This covers cases where hitobjects aren't assigned "difficulty" representations because they don't meaningfully contribute to the calculations. - HitObject parent = hitObjectParentLinks[hitObject.BaseObject]; - while (progressiveBeatmap.HitObjects.LastOrDefault() != parent) - progressiveBeatmap.HitObjects.Add(Beatmap.HitObjects[progressiveBeatmap.HitObjects.Count]); + // Implementations expect the progressive beatmap to only contain top-level objects from the original beatmap. + // At the same time, we also need to consider the possibility DHOs may not be generated for any given object, + // so we'll add all remaining objects up to the current point in time to the progressive beatmap. + for (int i = progressiveBeatmap.HitObjects.Count; i < Beatmap.HitObjects.Count; i++) + { + if (Beatmap.HitObjects[i].StartTime > hitObject.BaseObject.StartTime) + break; + + progressiveBeatmap.HitObjects.Add(Beatmap.HitObjects[i]); + } foreach (var skill in skills) { @@ -133,23 +131,6 @@ namespace osu.Game.Rulesets.Difficulty } return attribs; - - static IEnumerable<(HitObject obj, HitObject mostParentingObject)> createHitObjectParentLinks(IBeatmap beatmap) - { - foreach (var link in createNestedLinks(beatmap.HitObjects, null)) - yield return link; - - static IEnumerable<(HitObject obj, HitObject mostParentingObject)> createNestedLinks(IReadOnlyList objects, [CanBeNull] HitObject parent) - { - foreach (var o in objects) - { - yield return (o, parent ?? o); - - foreach (var n in createNestedLinks(o.NestedHitObjects, parent ?? o)) - yield return n; - } - } - } } /// From e9b319f4c6e43d2488cf4ebee2c9d56f7f828b70 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 12 Apr 2024 00:11:54 +0900 Subject: [PATCH 199/581] Ensure all remaining objects are added in the last iteration --- .../TestSceneTimedDifficultyCalculation.cs | 27 +++++++++++++++++++ .../Difficulty/DifficultyCalculator.cs | 9 ++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs index 4c32d3bf1c..1a75f735ef 100644 --- a/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs +++ b/osu.Game.Tests/NonVisual/TestSceneTimedDifficultyCalculation.cs @@ -99,6 +99,33 @@ namespace osu.Game.Tests.NonVisual assertEquals(attribs[1], beatmap.HitObjects[0]); } + [Test] + public void TestSkippedLastObjectAddedInLastIteration() + { + var beatmap = new Beatmap + { + HitObjects = + { + new TestHitObject { StartTime = 1 }, + new TestHitObject + { + StartTime = 2, + Skip = true + }, + new TestHitObject + { + StartTime = 3, + Skip = true + }, + } + }; + + List attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed(); + + Assert.That(attribs.Count, Is.EqualTo(1)); + assertEquals(attribs[0], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]); + } + private void assertEquals(TimedDifficultyAttributes attribs, params HitObject[] expected) { Assert.That(((TestDifficultyAttributes)attribs.Attributes).Objects, Is.EquivalentTo(expected)); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5d608deae2..1599dff8d9 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -107,15 +107,16 @@ namespace osu.Game.Rulesets.Difficulty var skills = CreateSkills(Beatmap, playableMods, clockRate); var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap); + var difficultyObjects = getDifficultyHitObjects().ToArray(); - foreach (var hitObject in getDifficultyHitObjects()) + foreach (var obj in difficultyObjects) { // Implementations expect the progressive beatmap to only contain top-level objects from the original beatmap. // At the same time, we also need to consider the possibility DHOs may not be generated for any given object, // so we'll add all remaining objects up to the current point in time to the progressive beatmap. for (int i = progressiveBeatmap.HitObjects.Count; i < Beatmap.HitObjects.Count; i++) { - if (Beatmap.HitObjects[i].StartTime > hitObject.BaseObject.StartTime) + if (obj != difficultyObjects[^1] && Beatmap.HitObjects[i].StartTime > obj.BaseObject.StartTime) break; progressiveBeatmap.HitObjects.Add(Beatmap.HitObjects[i]); @@ -124,10 +125,10 @@ namespace osu.Game.Rulesets.Difficulty foreach (var skill in skills) { cancellationToken.ThrowIfCancellationRequested(); - skill.Process(hitObject); + skill.Process(obj); } - attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); + attribs.Add(new TimedDifficultyAttributes(obj.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); } return attribs; From 8b2017be453fb03905bc857bd117c2555cb05e69 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 12 Apr 2024 01:02:40 +0900 Subject: [PATCH 200/581] Update Sentry to fix iOS build --- osu.Game/Utils/SentryLogger.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 61622a7122..896f4daf33 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -64,7 +64,7 @@ namespace osu.Game.Utils localUser = user.GetBoundCopy(); localUser.BindValueChanged(u => { - SentrySdk.ConfigureScope(scope => scope.User = new User + SentrySdk.ConfigureScope(scope => scope.User = new SentryUser { Username = u.NewValue.Username, Id = u.NewValue.Id.ToString(), diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3ea756a8b8..21b5bc60a5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 3ec93745a45127a204b1b25df81784bc1392ac2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Apr 2024 01:07:52 +0800 Subject: [PATCH 201/581] Fix test failures due to sentry oversight --- osu.Game/Utils/SentryLogger.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 896f4daf33..8d3e5fb834 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -39,12 +39,13 @@ namespace osu.Game.Utils public SentryLogger(OsuGame game) { this.game = game; + + if (!game.IsDeployedBuild || !game.CreateEndpoints().WebsiteRootUrl.EndsWith(@".ppy.sh", StringComparison.Ordinal)) + return; + sentrySession = SentrySdk.Init(options => { - // Not setting the dsn will completely disable sentry. - if (game.IsDeployedBuild && game.CreateEndpoints().WebsiteRootUrl.EndsWith(@".ppy.sh", StringComparison.Ordinal)) - options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2"; - + options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2"; options.AutoSessionTracking = true; options.IsEnvironmentUser = false; options.IsGlobalModeEnabled = true; @@ -59,6 +60,9 @@ namespace osu.Game.Utils public void AttachUser(IBindable user) { + if (sentrySession == null) + return; + Debug.Assert(localUser == null); localUser = user.GetBoundCopy(); From c0dce94f1593ef13d3d9bb214d2ef32331da1f07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Apr 2024 16:24:46 +0800 Subject: [PATCH 202/581] Fix newly placed items in skin editor not getting correct anchor placement --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 619eac8f4a..bc929177d1 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -454,6 +454,7 @@ namespace osu.Game.Overlays.SkinEditor } SelectedComponents.Add(component); + SkinSelectionHandler.ApplyClosestAnchor(drawableComponent); return true; } @@ -666,8 +667,6 @@ namespace osu.Game.Overlays.SkinEditor SelectedComponents.Clear(); placeComponent(sprite, false); - - SkinSelectionHandler.ApplyClosestAnchor(sprite); }); return Task.CompletedTask; From c7f3a599c98f49e821e5790a436ff508e90ce097 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 13 Apr 2024 13:17:06 +0900 Subject: [PATCH 203/581] Fix crash when entering multiplayer on macOS --- osu.Desktop/DiscordRichPresence.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index ed9d4ca2d2..f1c796d0cd 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -205,7 +205,9 @@ namespace osu.Desktop Password = room.Settings.Password, }; - presence.Secrets.JoinSecret = JsonConvert.SerializeObject(roomSecret); + if (client.HasRegisteredUriScheme) + presence.Secrets.JoinSecret = JsonConvert.SerializeObject(roomSecret); + // discord cannot handle both secrets and buttons at the same time, so we need to choose something. // the multiplayer room seems more important. presence.Buttons = null; From feb9b5bdb8a74e566e35d00ce23e492f129b6f05 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 13 Apr 2024 13:42:57 +0300 Subject: [PATCH 204/581] Make traceable pp match HD --- .../Difficulty/OsuPerformanceCalculator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 4771bce280..e7e9308eb5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty } else if (score.Mods.Any(h => h is OsuModTraceable)) { - // Default 2% increase and another is scaled by AR - aimValue *= 1.02 + 0.02 * (12.0 - attributes.ApproachRate); + // The same as HD, placeholder bonus + aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator. @@ -174,8 +174,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty } else if (score.Mods.Any(h => h is OsuModTraceable)) { - // More reward for speed because speed on Traceable is annoying - speedValue *= 1.04 + 0.06 * (12.0 - attributes.ApproachRate); + // The same as HD, placeholder bonus + speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } // Calculate accuracy assuming the worst case scenario @@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty else if (score.Mods.Any(m => m is OsuModHidden)) accuracyValue *= 1.08; else if (score.Mods.Any(m => m is OsuModTraceable)) - accuracyValue *= 1.02 + 0.01 * (12.0 - attributes.ApproachRate); + accuracyValue *= 1.08; if (score.Mods.Any(m => m is OsuModFlashlight)) accuracyValue *= 1.02; From 4a21ff97263ac0219905e7a4fc51b8d5e1c55705 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 13 Apr 2024 13:59:09 +0300 Subject: [PATCH 205/581] removed duplication --- .../Difficulty/OsuPerformanceCalculator.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e7e9308eb5..18a4b8be0c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -111,16 +111,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (score.Mods.Any(m => m is OsuModBlinds)) aimValue *= 1.3 + (totalHits * (0.0016 / (1 + 2 * effectiveMissCount)) * Math.Pow(accuracy, 16)) * (1 - 0.003 * attributes.DrainRate * attributes.DrainRate); - else if (score.Mods.Any(h => h is OsuModHidden)) + else if (score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable)) { // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } - else if (score.Mods.Any(h => h is OsuModTraceable)) - { - // The same as HD, placeholder bonus - aimValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); - } // We assume 15% of sliders in a map are difficult since there's no way to tell from the performance calculator. double estimateDifficultSliders = attributes.SliderCount * 0.15; @@ -167,16 +162,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Increasing the speed value by object count for Blinds isn't ideal, so the minimum buff is given. speedValue *= 1.12; } - else if (score.Mods.Any(m => m is OsuModHidden)) + else if (score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable)) { // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); } - else if (score.Mods.Any(h => h is OsuModTraceable)) - { - // The same as HD, placeholder bonus - speedValue *= 1.0 + 0.04 * (12.0 - attributes.ApproachRate); - } // Calculate accuracy assuming the worst case scenario double relevantTotalDiff = totalHits - attributes.SpeedNoteCount; @@ -222,9 +212,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Increasing the accuracy value by object count for Blinds isn't ideal, so the minimum buff is given. if (score.Mods.Any(m => m is OsuModBlinds)) accuracyValue *= 1.14; - else if (score.Mods.Any(m => m is OsuModHidden)) - accuracyValue *= 1.08; - else if (score.Mods.Any(m => m is OsuModTraceable)) + else if (score.Mods.Any(m => m is OsuModHidden || m is OsuModTraceable)) accuracyValue *= 1.08; if (score.Mods.Any(m => m is OsuModFlashlight)) From 5a8b8908dd5ba8064f87d803fb1308340139582a Mon Sep 17 00:00:00 2001 From: Loreos7 Date: Sat, 13 Apr 2024 14:53:51 +0300 Subject: [PATCH 206/581] fix missing underscore --- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 8ccfd039ec..6c5f7fab9e 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -152,7 +152,7 @@ namespace osu.Game.Screens.Play Logger.Log($"Please ensure that you are using the latest version of the official game releases.\n\n{whatWillHappen}", level: LogLevel.Important); break; - case @"invalid beatmap hash": + case @"invalid beatmap_hash": Logger.Log($"This beatmap does not match the online version. Please update or redownload it.\n\n{whatWillHappen}", level: LogLevel.Important); break; From 9833dd955f8c09ff8455ab499257ac044362414c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vaj=C4=8F=C3=A1k?= Date: Sun, 14 Apr 2024 01:30:59 +0200 Subject: [PATCH 207/581] Fix toolbar volume bar masking --- osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 5da0056787..718789e3c7 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Toolbar { public partial class ToolbarMusicButton : ToolbarOverlayToggleButton { - private Circle volumeBar; + private Box volumeBar; protected override Anchor TooltipAnchor => Anchor.TopRight; @@ -45,14 +45,15 @@ namespace osu.Game.Overlays.Toolbar Height = IconContainer.Height, Margin = new MarginPadding { Horizontal = 2.5f }, Masking = true, - Children = new[] + CornerRadius = 3f, + Children = new Drawable[] { new Circle { RelativeSizeAxes = Axes.Both, Colour = Color4.White.Opacity(0.25f), }, - volumeBar = new Circle + volumeBar = new Box { RelativeSizeAxes = Axes.Both, Height = 0f, From ed6680a61d535849beb78bd26738e55172e2aabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vaj=C4=8F=C3=A1k?= Date: Sun, 14 Apr 2024 15:10:05 +0200 Subject: [PATCH 208/581] Fixed type inconsistency and rounding --- osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 718789e3c7..51b95b7d32 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Toolbar StateContainer = music; Flow.Padding = new MarginPadding { Horizontal = Toolbar.HEIGHT / 4 }; - Flow.Add(volumeDisplay = new Container + Flow.Add(volumeDisplay = new CircularContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -45,10 +45,9 @@ namespace osu.Game.Overlays.Toolbar Height = IconContainer.Height, Margin = new MarginPadding { Horizontal = 2.5f }, Masking = true, - CornerRadius = 3f, - Children = new Drawable[] + Children = new[] { - new Circle + new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.White.Opacity(0.25f), From f282152f996b3e15dffa9ae7564c1e09788430dd Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 14 Apr 2024 15:53:29 -0700 Subject: [PATCH 209/581] Enable NRT to `ScoreManager` --- .../Database/BackgroundDataStoreProcessor.cs | 17 ++++++++++------- osu.Game/Scoring/ScoreManager.cs | 18 ++++++++---------- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/osu.Game/Database/BackgroundDataStoreProcessor.cs b/osu.Game/Database/BackgroundDataStoreProcessor.cs index 872194aa1d..52336c0242 100644 --- a/osu.Game/Database/BackgroundDataStoreProcessor.cs +++ b/osu.Game/Database/BackgroundDataStoreProcessor.cs @@ -287,14 +287,17 @@ namespace osu.Game.Database { var score = scoreManager.Query(s => s.ID == id); - scoreManager.PopulateMaximumStatistics(score); - - // Can't use async overload because we're not on the update thread. - // ReSharper disable once MethodHasAsyncOverload - realmAccess.Write(r => + if (score != null) { - r.Find(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics); - }); + scoreManager.PopulateMaximumStatistics(score); + + // Can't use async overload because we're not on the update thread. + // ReSharper disable once MethodHasAsyncOverload + realmAccess.Write(r => + { + r.Find(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics); + }); + } ++processedCount; } diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 1ee99e9e93..1cdf4b0c13 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; @@ -28,7 +26,7 @@ namespace osu.Game.Scoring public class ScoreManager : ModelManager, IModelImporter { private readonly Func beatmaps; - private readonly OsuConfigManager configManager; + private readonly OsuConfigManager? configManager; private readonly ScoreImporter scoreImporter; private readonly LegacyScoreExporter scoreExporter; @@ -43,7 +41,7 @@ namespace osu.Game.Scoring } public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, RealmAccess realm, IAPIProvider api, - OsuConfigManager configManager = null) + OsuConfigManager? configManager = null) : base(storage, realm) { this.beatmaps = beatmaps; @@ -67,7 +65,7 @@ namespace osu.Game.Scoring /// /// The query. /// The first result for the provided query, or null if no results were found. - public ScoreInfo Query(Expression> query) + public ScoreInfo? Query(Expression> query) { return Realm.Run(r => r.All().FirstOrDefault(query)?.Detach()); } @@ -104,7 +102,7 @@ namespace osu.Game.Scoring /// /// The to provide the total score of. /// The config. - public TotalScoreBindable(ScoreInfo score, OsuConfigManager configManager) + public TotalScoreBindable(ScoreInfo score, OsuConfigManager? configManager) { configManager?.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); scoringMode.BindValueChanged(mode => Value = score.GetDisplayScore(mode.NewValue), true); @@ -126,7 +124,7 @@ namespace osu.Game.Scoring } } - public void Delete([CanBeNull] Expression> filter = null, bool silent = false) + public void Delete(Expression>? filter = null, bool silent = false) { Realm.Run(r => { @@ -165,9 +163,9 @@ namespace osu.Game.Scoring public Task Export(ScoreInfo score) => scoreExporter.ExportAsync(score.ToLive(Realm)); - public Task> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); + public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); - public Live Import(ScoreInfo item, ArchiveReader archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => + public Live? Import(ScoreInfo item, ArchiveReader? archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => scoreImporter.ImportModel(item, archive, parameters, cancellationToken); /// @@ -182,7 +180,7 @@ namespace osu.Game.Scoring #region Implementation of IPresentImports - public Action>> PresentImport + public Action>>? PresentImport { set => scoreImporter.PresentImport = value; } diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index ef27aac1b9..4f665b87e8 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Play { if (state.NewValue != DownloadState.LocallyAvailable) return; - scoreManager.Export(importedScore); + if (importedScore != null) scoreManager.Export(importedScore); this.state.ValueChanged -= exportWhenReady; } From ed8b59632561cfbbfa7e949daa4709660cc3356d Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 14 Apr 2024 16:22:58 -0700 Subject: [PATCH 210/581] Fix replay export not working correctly from online leaderboards --- osu.Game/OsuGame.cs | 19 ++--------- osu.Game/Scoring/ScoreManager.cs | 34 +++++++++++++++++-- .../Screens/Ranking/ReplayDownloadButton.cs | 4 ++- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 732d5f867c..bcf12b308d 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -706,24 +706,9 @@ namespace osu.Game { Logger.Log($"Beginning {nameof(PresentScore)} with score {score}"); - // The given ScoreInfo may have missing properties if it was retrieved from online data. Re-retrieve it from the database - // to ensure all the required data for presenting a replay are present. - ScoreInfo databasedScoreInfo = null; + ScoreInfo databasedScoreInfo = ScoreManager.GetDatabasedScoreInfo(score); - if (score.OnlineID > 0) - databasedScoreInfo = ScoreManager.Query(s => s.OnlineID == score.OnlineID); - - if (score.LegacyOnlineID > 0) - databasedScoreInfo ??= ScoreManager.Query(s => s.LegacyOnlineID == score.LegacyOnlineID); - - if (score is ScoreInfo scoreInfo) - databasedScoreInfo ??= ScoreManager.Query(s => s.Hash == scoreInfo.Hash); - - if (databasedScoreInfo == null) - { - Logger.Log("The requested score could not be found locally.", LoggingTarget.Information); - return; - } + if (databasedScoreInfo == null) return; var databasedScore = ScoreManager.GetScore(databasedScoreInfo); diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 1cdf4b0c13..f699e32ac7 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -8,8 +8,8 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -70,6 +70,34 @@ namespace osu.Game.Scoring return Realm.Run(r => r.All().FirstOrDefault(query)?.Detach()); } + /// + /// Re-retrieve a given from the database to ensure all the required data for presenting / exporting a replay are present. + /// + /// The to attempt querying on. + /// The databased score info. Null if the score on the database cannot be found. + /// Can be used when the was retrieved from online data, as it may have missing properties. + public ScoreInfo? GetDatabasedScoreInfo(IScoreInfo originalScoreInfo) + { + ScoreInfo? databasedScoreInfo = null; + + if (originalScoreInfo.OnlineID > 0) + databasedScoreInfo = Query(s => s.OnlineID == originalScoreInfo.OnlineID); + + if (originalScoreInfo.LegacyOnlineID > 0) + databasedScoreInfo ??= Query(s => s.LegacyOnlineID == originalScoreInfo.LegacyOnlineID); + + if (originalScoreInfo is ScoreInfo scoreInfo) + databasedScoreInfo ??= Query(s => s.Hash == scoreInfo.Hash); + + if (databasedScoreInfo == null) + { + Logger.Log("The requested score could not be found locally.", LoggingTarget.Information); + return null; + } + + return databasedScoreInfo; + } + /// /// Retrieves a bindable that represents the total score of a . /// @@ -78,7 +106,7 @@ namespace osu.Game.Scoring /// /// The to retrieve the bindable for. /// The bindable containing the total score. - public Bindable GetBindableTotalScore([NotNull] ScoreInfo score) => new TotalScoreBindable(score, configManager); + public Bindable GetBindableTotalScore(ScoreInfo score) => new TotalScoreBindable(score, configManager); /// /// Retrieves a bindable that represents the formatted total score string of a . @@ -88,7 +116,7 @@ namespace osu.Game.Scoring /// /// The to retrieve the bindable for. /// The bindable containing the formatted total score string. - public Bindable GetBindableTotalScoreString([NotNull] ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); + public Bindable GetBindableTotalScoreString(ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); /// /// Provides the total score of a . Responds to changes in the currently-selected . diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index df5f9c7a8a..9bacfc5ed3 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -147,7 +147,9 @@ namespace osu.Game.Screens.Ranking { if (state.NewValue != DownloadState.LocallyAvailable) return; - scoreManager.Export(Score.Value); + ScoreInfo? databasedScoreInfo = scoreManager.GetDatabasedScoreInfo(Score.Value); + + if (databasedScoreInfo != null) scoreManager.Export(databasedScoreInfo); State.ValueChanged -= exportWhenReady; } From 8506da725dcf0aac225bd6ab5c230fb0150827e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Apr 2024 11:49:47 +0200 Subject: [PATCH 211/581] Add failing test --- .../TestSceneExpandedPanelMiddleContent.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 9f7726313a..02a321d22f 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -104,6 +104,21 @@ namespace osu.Game.Tests.Visual.Ranking }); } + [Test] + public void TestPPNotShownAsProvisionalIfClassicModIsPresentDueToLegacyScore() + { + AddStep("show example score", () => + { + var score = TestResources.CreateTestScoreInfo(createTestBeatmap(new RealmUser())); + score.PP = 400; + score.Mods = score.Mods.Append(new OsuModClassic()).ToArray(); + score.IsLegacyScore = true; + showPanel(score); + }); + + AddAssert("pp display faded out", () => this.ChildrenOfType().Single().Alpha == 1); + } + [Test] public void TestWithDefaultDate() { From 7c4c8ee75c746620f955f1fbb2b1e43f26badf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Apr 2024 11:53:03 +0200 Subject: [PATCH 212/581] Fix stable scores showing with faded out pp display due to classic mod presence --- .../Expanded/Statistics/PerformanceStatistic.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 0a9c68eafc..8366f8d7ef 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,6 +18,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osu.Game.Localisation; +using osu.Game.Rulesets.Mods; namespace osu.Game.Screens.Ranking.Expanded.Statistics { @@ -74,7 +76,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics Alpha = 0.5f; TooltipText = ResultsScreenStrings.NoPPForUnrankedBeatmaps; } - else if (scoreInfo.Mods.Any(m => !m.Ranked)) + else if (hasUnrankedMods(scoreInfo)) { Alpha = 0.5f; TooltipText = ResultsScreenStrings.NoPPForUnrankedMods; @@ -87,6 +89,16 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics } } + private static bool hasUnrankedMods(ScoreInfo scoreInfo) + { + IEnumerable modsToCheck = scoreInfo.Mods; + + if (scoreInfo.IsLegacyScore) + modsToCheck = modsToCheck.Where(m => m is not ModClassic); + + return modsToCheck.Any(m => !m.Ranked); + } + public override void Appear() { base.Appear(); From fe7df808b6065dcbb405f7775fdaaa0d5a441f52 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Apr 2024 21:07:08 +0900 Subject: [PATCH 213/581] Add tests --- .../SongSelect/TestScenePlaySongSelect.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index ce241f3676..e03ffd48f1 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -29,6 +29,7 @@ using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; @@ -1147,6 +1148,62 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("filter text cleared", () => songSelect!.FilterControl.ChildrenOfType().First().Text, () => Is.Empty); } + [Test] + public void TestNonFilterableModChange() + { + addRulesetImportStep(0); + + createSongSelect(); + + // Mod that is guaranteed to never re-filter. + AddStep("add non-filterable mod", () => SelectedMods.Value = new Mod[] { new OsuModCinema() }); + AddAssert("filter count is 1", () => songSelect!.FilterCount, () => Is.EqualTo(1)); + + // Removing the mod should still not re-filter. + AddStep("remove non-filterable mod", () => SelectedMods.Value = Array.Empty()); + AddAssert("filter count is 1", () => songSelect!.FilterCount, () => Is.EqualTo(1)); + } + + [Test] + public void TestFilterableModChange() + { + addRulesetImportStep(3); + + createSongSelect(); + + // Change to mania ruleset. + AddStep("filter to mania ruleset", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 3)); + AddAssert("filter count is 2", () => songSelect!.FilterCount, () => Is.EqualTo(2)); + + // Apply a mod, but this should NOT re-filter because there's no search text. + AddStep("add filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey3() }); + AddAssert("filter count is 2", () => songSelect!.FilterCount, () => Is.EqualTo(2)); + + // Set search text. Should re-filter. + AddStep("set search text to match mods", () => songSelect!.FilterControl.CurrentTextSearch.Value = "keys=3"); + AddAssert("filter count is 3", () => songSelect!.FilterCount, () => Is.EqualTo(3)); + + // Change filterable mod. Should re-filter. + AddStep("change new filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey5() }); + AddAssert("filter count is 4", () => songSelect!.FilterCount, () => Is.EqualTo(4)); + + // Add non-filterable mod. Should NOT re-filter. + AddStep("apply non-filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModNoFail(), new ManiaModKey5() }); + AddAssert("filter count is 4", () => songSelect!.FilterCount, () => Is.EqualTo(4)); + + // Remove filterable mod. Should re-filter. + AddStep("remove filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModNoFail() }); + AddAssert("filter count is 5", () => songSelect!.FilterCount, () => Is.EqualTo(5)); + + // Remove non-filterable mod. Should NOT re-filter. + AddStep("remove filterable mod", () => SelectedMods.Value = Array.Empty()); + AddAssert("filter count is 5", () => songSelect!.FilterCount, () => Is.EqualTo(5)); + + // Add filterable mod. Should re-filter. + AddStep("add filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey3() }); + AddAssert("filter count is 6", () => songSelect!.FilterCount, () => Is.EqualTo(6)); + } + private void waitForInitialSelection() { AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); From 343b3ba0e614dcfd68e9f3b6046b7d119776c95e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Apr 2024 21:07:36 +0900 Subject: [PATCH 214/581] Don't re-filter unless mods may change the filter --- .../ManiaFilterCriteria.cs | 20 +++++++++++++++++++ .../NonVisual/Filtering/FilterMatchingTest.cs | 5 +++++ .../Filtering/FilterQueryParserTest.cs | 5 +++++ .../Rulesets/Filter/IRulesetFilterCriteria.cs | 10 ++++++++++ osu.Game/Screens/Select/FilterControl.cs | 13 ++++++------ 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index ea7eb5b8f0..8c6efbc72d 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -1,9 +1,14 @@ // 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 System.Linq; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -30,5 +35,20 @@ namespace osu.Game.Rulesets.Mania return false; } + + public bool FilterMayChangeFromMods(ValueChangedEvent> mods) + { + if (keys.HasFilter) + { + // Interpreting as the Mod type is required for equality comparison. + HashSet oldSet = mods.OldValue.OfType().AsEnumerable().ToHashSet(); + HashSet newSet = mods.NewValue.OfType().AsEnumerable().ToHashSet(); + + if (!oldSet.SetEquals(newSet)) + return true; + } + + return false; + } } } diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs index 78d8eabba7..10e0e46f4c 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs @@ -1,10 +1,13 @@ // 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 NUnit.Framework; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; @@ -311,6 +314,8 @@ namespace osu.Game.Tests.NonVisual.Filtering public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) => match; public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false; + + public bool FilterMayChangeFromMods(ValueChangedEvent> mods) => false; } } } diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index b0ceed45b9..7897b3d8c0 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -2,10 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; @@ -514,6 +517,8 @@ namespace osu.Game.Tests.NonVisual.Filtering return false; } + + public bool FilterMayChangeFromMods(ValueChangedEvent> mods) => false; } private static readonly object[] correct_date_query_examples = diff --git a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs index f926b04db4..c374fe315d 100644 --- a/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs +++ b/osu.Game/Rulesets/Filter/IRulesetFilterCriteria.cs @@ -1,7 +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 osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -52,5 +55,12 @@ namespace osu.Game.Rulesets.Filter /// while ignored criteria are included in . /// bool TryParseCustomKeywordCriteria(string key, Operator op, string value); + + /// + /// Whether to reapply the filter as a result of the given change in applied mods. + /// + /// The change in mods. + /// Whether the filter should be re-applied. + bool FilterMayChangeFromMods(ValueChangedEvent> mods); } } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 0bfd927234..73c122dda6 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -49,15 +50,14 @@ namespace osu.Game.Screens.Select } private OsuTabControl sortTabs; - private Bindable sortMode; - private Bindable groupMode; - private FilterControlTextBox searchTextBox; - private CollectionDropdown collectionDropdown; + [CanBeNull] + private FilterCriteria currentCriteria; + public FilterCriteria CreateCriteria() { string query = searchTextBox.Text; @@ -228,7 +228,8 @@ namespace osu.Game.Screens.Select if (m.NewValue.SequenceEqual(m.OldValue)) return; - updateCriteria(); + if (currentCriteria?.RulesetCriteria?.FilterMayChangeFromMods(m) == true) + updateCriteria(); }); groupMode.BindValueChanged(_ => updateCriteria()); @@ -263,7 +264,7 @@ namespace osu.Game.Screens.Select private readonly Bindable minimumStars = new BindableDouble(); private readonly Bindable maximumStars = new BindableDouble(); - private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); + private void updateCriteria() => FilterChanged?.Invoke(currentCriteria = CreateCriteria()); protected override bool OnClick(ClickEvent e) => true; From 7e4782d4b1aeec427ea41d3a5d3bfe5d25a2f1f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Apr 2024 09:50:51 +0800 Subject: [PATCH 215/581] Allow nested high performance sessions Mostly just for safety, since I noticed this would pretty much fall over in this scenario until now. --- .../HighPerformanceSessionManager.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/Performance/HighPerformanceSessionManager.cs b/osu.Desktop/Performance/HighPerformanceSessionManager.cs index eb2b3be5b9..058d247aee 100644 --- a/osu.Desktop/Performance/HighPerformanceSessionManager.cs +++ b/osu.Desktop/Performance/HighPerformanceSessionManager.cs @@ -11,16 +11,24 @@ namespace osu.Desktop.Performance { public class HighPerformanceSessionManager : IHighPerformanceSessionManager { + private int activeSessions; + private GCLatencyMode originalGCMode; public IDisposable BeginSession() { - enableHighPerformanceSession(); - return new InvokeOnDisposal(this, static m => m.disableHighPerformanceSession()); + enterSession(); + return new InvokeOnDisposal(this, static m => m.exitSession()); } - private void enableHighPerformanceSession() + private void enterSession() { + if (Interlocked.Increment(ref activeSessions) > 1) + { + Logger.Log($"High performance session requested ({activeSessions} others already running)"); + return; + } + Logger.Log("Starting high performance session"); originalGCMode = GCSettings.LatencyMode; @@ -30,8 +38,14 @@ namespace osu.Desktop.Performance GC.Collect(0); } - private void disableHighPerformanceSession() + private void exitSession() { + if (Interlocked.Decrement(ref activeSessions) > 0) + { + Logger.Log($"High performance session finished ({activeSessions} others remain)"); + return; + } + Logger.Log("Ending high performance session"); if (GCSettings.LatencyMode == GCLatencyMode.LowLatency) From d89edd2b4f52fe776c77a66a414c1dcd2472bede Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Apr 2024 09:51:43 +0800 Subject: [PATCH 216/581] Expose high performance session state --- osu.Desktop/Performance/HighPerformanceSessionManager.cs | 3 +++ osu.Game/Performance/IHighPerformanceSessionManager.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/osu.Desktop/Performance/HighPerformanceSessionManager.cs b/osu.Desktop/Performance/HighPerformanceSessionManager.cs index 058d247aee..34762de04d 100644 --- a/osu.Desktop/Performance/HighPerformanceSessionManager.cs +++ b/osu.Desktop/Performance/HighPerformanceSessionManager.cs @@ -3,6 +3,7 @@ using System; using System.Runtime; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Game.Performance; @@ -11,6 +12,8 @@ namespace osu.Desktop.Performance { public class HighPerformanceSessionManager : IHighPerformanceSessionManager { + public bool IsSessionActive => activeSessions > 0; + private int activeSessions; private GCLatencyMode originalGCMode; diff --git a/osu.Game/Performance/IHighPerformanceSessionManager.cs b/osu.Game/Performance/IHighPerformanceSessionManager.cs index d3d1fda8fc..cc995e4942 100644 --- a/osu.Game/Performance/IHighPerformanceSessionManager.cs +++ b/osu.Game/Performance/IHighPerformanceSessionManager.cs @@ -14,6 +14,11 @@ namespace osu.Game.Performance /// public interface IHighPerformanceSessionManager { + /// + /// Whether a high performance session is currently active. + /// + bool IsSessionActive { get; } + /// /// Start a new high performance session. /// From a651cb8d507c8720a83db85acef9738107046461 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Apr 2024 09:51:58 +0800 Subject: [PATCH 217/581] Stop background processing from running when inside a high performance session --- osu.Game/Database/BackgroundDataStoreProcessor.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/BackgroundDataStoreProcessor.cs b/osu.Game/Database/BackgroundDataStoreProcessor.cs index 872194aa1d..f3b37f608c 100644 --- a/osu.Game/Database/BackgroundDataStoreProcessor.cs +++ b/osu.Game/Database/BackgroundDataStoreProcessor.cs @@ -16,6 +16,7 @@ using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Performance; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; @@ -51,6 +52,9 @@ namespace osu.Game.Database [Resolved] private ILocalUserPlayInfo? localUserPlayInfo { get; set; } + [Resolved] + private IHighPerformanceSessionManager? highPerformanceSessionManager { get; set; } + [Resolved] private INotificationOverlay? notificationOverlay { get; set; } @@ -493,7 +497,9 @@ namespace osu.Game.Database private void sleepIfRequired() { - while (localUserPlayInfo?.IsPlaying.Value == true) + // Importantly, also sleep if high performance session is active. + // If we don't do this, memory usage can become runaway due to GC running in a more lenient mode. + while (localUserPlayInfo?.IsPlaying.Value == true || highPerformanceSessionManager?.IsSessionActive == true) { Logger.Log("Background processing sleeping due to active gameplay..."); Thread.Sleep(TimeToSleepDuringGameplay); From 67cfcddc7727fb5a735cff9ce3dab8d0bae0e004 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 15 Apr 2024 20:18:24 -0700 Subject: [PATCH 218/581] Use another beatmap query to not depend on databased score info --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bcf12b308d..6a29767a8e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -718,7 +718,7 @@ namespace osu.Game return; } - var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScoreInfo.BeatmapInfo.ID); + var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.OnlineID == score.Beatmap.OnlineID); if (databasedBeatmap == null) { From 514e316b49ad9721e0e7997d02fc14dae3c86504 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 15 Apr 2024 20:48:51 -0700 Subject: [PATCH 219/581] Make `getDatabasedScoreInfo()` private and move to `GetScore()` and `Export()` --- osu.Game/OsuGame.cs | 6 ++---- osu.Game/Scoring/ScoreManager.cs | 16 +++++++++++++--- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 4 +--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6a29767a8e..ccdf9d151f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -706,11 +706,9 @@ namespace osu.Game { Logger.Log($"Beginning {nameof(PresentScore)} with score {score}"); - ScoreInfo databasedScoreInfo = ScoreManager.GetDatabasedScoreInfo(score); + var databasedScore = ScoreManager.GetScore(score); - if (databasedScoreInfo == null) return; - - var databasedScore = ScoreManager.GetScore(databasedScoreInfo); + if (databasedScore == null) return; if (databasedScore.Replay == null) { diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index f699e32ac7..3f6c6ee49d 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -58,7 +58,12 @@ namespace osu.Game.Scoring }; } - public Score GetScore(ScoreInfo score) => scoreImporter.GetScore(score); + public Score? GetScore(IScoreInfo scoreInfo) + { + ScoreInfo? databasedScoreInfo = getDatabasedScoreInfo(scoreInfo); + + return databasedScoreInfo == null ? null : scoreImporter.GetScore(databasedScoreInfo); + } /// /// Perform a lookup query on available s. @@ -76,7 +81,7 @@ namespace osu.Game.Scoring /// The to attempt querying on. /// The databased score info. Null if the score on the database cannot be found. /// Can be used when the was retrieved from online data, as it may have missing properties. - public ScoreInfo? GetDatabasedScoreInfo(IScoreInfo originalScoreInfo) + private ScoreInfo? getDatabasedScoreInfo(IScoreInfo originalScoreInfo) { ScoreInfo? databasedScoreInfo = null; @@ -189,7 +194,12 @@ namespace osu.Game.Scoring public Task>> Import(ProgressNotification notification, ImportTask[] tasks, ImportParameters parameters = default) => scoreImporter.Import(notification, tasks); - public Task Export(ScoreInfo score) => scoreExporter.ExportAsync(score.ToLive(Realm)); + public Task? Export(ScoreInfo scoreInfo) + { + ScoreInfo? databasedScoreInfo = getDatabasedScoreInfo(scoreInfo); + + return databasedScoreInfo == null ? null : scoreExporter.ExportAsync(databasedScoreInfo.ToLive(Realm)); + } public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index 9bacfc5ed3..df5f9c7a8a 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -147,9 +147,7 @@ namespace osu.Game.Screens.Ranking { if (state.NewValue != DownloadState.LocallyAvailable) return; - ScoreInfo? databasedScoreInfo = scoreManager.GetDatabasedScoreInfo(Score.Value); - - if (databasedScoreInfo != null) scoreManager.Export(databasedScoreInfo); + scoreManager.Export(Score.Value); State.ValueChanged -= exportWhenReady; } From c7b1524b9ff6d4294b09eb2c3fc4fc4f04881a8f Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 16 Apr 2024 05:25:52 -0300 Subject: [PATCH 220/581] Add new audio samples --- .../Resources/Samples/test-sample.ogg | Bin 0 -> 36347 bytes .../Resources/Samples/test-sample.webm | Bin 0 -> 34247 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Tests/Resources/Samples/test-sample.ogg create mode 100644 osu.Game.Tests/Resources/Samples/test-sample.webm diff --git a/osu.Game.Tests/Resources/Samples/test-sample.ogg b/osu.Game.Tests/Resources/Samples/test-sample.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b33119cfaf89615cece2655a30c95c82127093ee GIT binary patch literal 36347 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Resources/Samples/test-sample.webm b/osu.Game.Tests/Resources/Samples/test-sample.webm new file mode 100644 index 0000000000000000000000000000000000000000..3964d248f4dc6fc0f84a6f3f4fcfe9db97461efa GIT binary patch literal 34247 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�tSAml_ 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 Date: Tue, 16 Apr 2024 16:37:11 +0800 Subject: [PATCH 221/581] Update HighPerformanceSessionManager.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Desktop/Performance/HighPerformanceSessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Performance/HighPerformanceSessionManager.cs b/osu.Desktop/Performance/HighPerformanceSessionManager.cs index 34762de04d..0df87ab007 100644 --- a/osu.Desktop/Performance/HighPerformanceSessionManager.cs +++ b/osu.Desktop/Performance/HighPerformanceSessionManager.cs @@ -28,7 +28,7 @@ namespace osu.Desktop.Performance { if (Interlocked.Increment(ref activeSessions) > 1) { - Logger.Log($"High performance session requested ({activeSessions} others already running)"); + Logger.Log($"High performance session requested ({activeSessions} running in total)"); return; } From 9ef27104ce50c5844004659b3782d984f3c05c8f Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 16 Apr 2024 06:15:21 -0300 Subject: [PATCH 222/581] Add checks for audio formats --- .../Checks/CheckHitsoundsFormatTest.cs | 96 +++++++++++++++++ .../Editing/Checks/CheckSongFormatTest.cs | 100 ++++++++++++++++++ osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 2 + .../Edit/Checks/CheckHitsoundsFormat.cs | 93 ++++++++++++++++ .../Rulesets/Edit/Checks/CheckSongFormat.cs | 81 ++++++++++++++ 5 files changed, 372 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs create mode 100644 osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs new file mode 100644 index 0000000000..912a7468f5 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs @@ -0,0 +1,96 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using ManagedBass; +using Moq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Resources; +using osuTK.Audio; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckHitsoundsFormatTest + { + private CheckHitsoundsFormat check = null!; + + private IBeatmap beatmap = null!; + + [SetUp] + public void Setup() + { + check = new CheckHitsoundsFormat(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo + { + Files = { CheckTestHelpers.CreateMockFile("wav") } + } + } + }; + + // 0 = No output device. This still allows decoding. + if (!Bass.Init(0) && Bass.LastError != Errors.Already) + throw new AudioException("Could not initialize Bass."); + } + + [Test] + public void TestMP3Audio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample-cut.mp3")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckHitsoundsFormat.IssueTemplateIncorrectFormat); + } + } + + [Test] + public void TestOGGAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample.ogg")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + + [Test] + public void TestWAVAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + + [Test] + public void TestWEBMAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample.webm")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckHitsoundsFormat.IssueTemplateFormatUnsupported); + } + } + + private BeatmapVerifierContext getContext(Stream? resourceStream) + { + var mockWorkingBeatmap = new Mock(beatmap, null, null); + mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny())).Returns(resourceStream); + + return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object); + } + } +} diff --git a/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs new file mode 100644 index 0000000000..acbf25ebad --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using ManagedBass; +using Moq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Resources; +using osuTK.Audio; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public partial class CheckSongFormatTest + { + private CheckSongFormat check = null!; + + private IBeatmap beatmap = null!; + + [SetUp] + public void Setup() + { + check = new CheckSongFormat(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo + { + Files = { CheckTestHelpers.CreateMockFile("mp3") } + } + } + }; + + // 0 = No output device. This still allows decoding. + if (!Bass.Init(0) && Bass.LastError != Errors.Already) + throw new AudioException("Could not initialize Bass."); + } + + [Test] + public void TestMP3Audio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample-cut.mp3")) + { + beatmap.Metadata.AudioFile = "abc123.mp3"; + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + + [Test] + public void TestOGGAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample.ogg")) + { + beatmap.Metadata.AudioFile = "abc123.mp3"; + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + + [Test] + public void TestWAVAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav")) + { + beatmap.Metadata.AudioFile = "abc123.mp3"; + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckSongFormat.IssueTemplateIncorrectFormat); + } + } + + [Test] + public void TestWEBMAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/test-sample.webm")) + { + beatmap.Metadata.AudioFile = "abc123.mp3"; + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckSongFormat.IssueTemplateFormatUnsupported); + } + } + + private BeatmapVerifierContext getContext(Stream? resourceStream) + { + var mockWorkingBeatmap = new Mock(beatmap, null, null); + mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny())).Returns(resourceStream); + + return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object); + } + } +} diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 7d3c7d0b2f..a9681e13ba 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Edit new CheckTooShortAudioFiles(), new CheckAudioInVideo(), new CheckDelayedHitsounds(), + new CheckSongFormat(), + new CheckHitsoundsFormat(), // Files new CheckZeroByteFiles(), diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs new file mode 100644 index 0000000000..e490a23963 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -0,0 +1,93 @@ +// 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 System.IO; +using System.Linq; +using ManagedBass; +using osu.Framework.Audio.Callbacks; +using osu.Game.Beatmaps; +using osu.Game.Extensions; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckHitsoundsFormat : ICheck + { + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Checks for hitsound formats."); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateFormatUnsupported(this), + new IssueTemplateIncorrectFormat(this), + }; + + private IEnumerable allowedFormats => new ChannelType[] + { + ChannelType.WavePCM, + ChannelType.WaveFloat, + ChannelType.OGG, + ChannelType.Wave | ChannelType.OGG, + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet; + var audioFile = beatmapSet?.GetFile(context.Beatmap.Metadata.AudioFile); + + if (beatmapSet == null) yield break; + + foreach (var file in beatmapSet.Files) + { + if (audioFile != null && file.File == audioFile.File) continue; + + using (Stream data = context.WorkingBeatmap.GetStream(file.File.GetStoragePath())) + { + if (data == null) + continue; + + var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); + int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Prescan, fileCallbacks.Callbacks, fileCallbacks.Handle); + + // If the format is not supported by BASS + if (decodeStream == 0) + { + if (AudioCheckUtils.HasAudioExtension(file.Filename) && probablyHasAudioData(data)) + yield return new IssueTemplateFormatUnsupported(this).Create(file.Filename); + + continue; + } + + var audioInfo = Bass.ChannelGetInfo(decodeStream); + + if (!allowedFormats.Contains(audioInfo.ChannelType)) + { + yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename, audioInfo.ChannelType.ToString()); + } + } + } + } + + private bool probablyHasAudioData(Stream data) => data.Length > 100; + + public class IssueTemplateFormatUnsupported : IssueTemplate + { + public IssueTemplateFormatUnsupported(ICheck check) + : base(check, IssueType.Problem, "\"{0}\" is using a unsupported format; Use wav or ogg for hitsounds.") + { + } + + public Issue Create(string file) => new Issue(this, file); + } + + public class IssueTemplateIncorrectFormat : IssueTemplate + { + public IssueTemplateIncorrectFormat(ICheck check) + : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}); Use wav or ogg for hitsounds.") + { + } + + public Issue Create(string file, string format) => new Issue(this, file, format); + } + } +} diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs new file mode 100644 index 0000000000..4162bf20a3 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -0,0 +1,81 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ManagedBass; +using osu.Framework.Audio.Callbacks; +using osu.Game.Beatmaps; +using osu.Game.Extensions; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckSongFormat : ICheck + { + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Checks for song formats."); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateFormatUnsupported(this), + new IssueTemplateIncorrectFormat(this), + }; + + private IEnumerable allowedFormats => new ChannelType[] + { + ChannelType.MP3, + ChannelType.OGG, + }; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet; + var audioFile = beatmapSet?.GetFile(context.Beatmap.Metadata.AudioFile); + + if (beatmapSet == null) yield break; + if (audioFile == null) yield break; + + using (Stream data = context.WorkingBeatmap.GetStream(audioFile.File.GetStoragePath())) + { + if (data == null || data.Length <= 0) yield break; + + var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); + int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Prescan, fileCallbacks.Callbacks, fileCallbacks.Handle); + + // If the format is not supported by BASS + if (decodeStream == 0) + { + yield return new IssueTemplateFormatUnsupported(this).Create(audioFile.Filename); + yield break; + } + + var audioInfo = Bass.ChannelGetInfo(decodeStream); + + if (!allowedFormats.Contains(audioInfo.ChannelType)) + yield return new IssueTemplateIncorrectFormat(this).Create(audioFile.Filename, audioInfo.ChannelType.ToString()); + } + } + + public class IssueTemplateFormatUnsupported : IssueTemplate + { + public IssueTemplateFormatUnsupported(ICheck check) + : base(check, IssueType.Problem, "\"{0}\" is using a unsupported format; Use mp3 or ogg for the song's audio.") + { + } + + public Issue Create(string file) => new Issue(this, file); + } + + public class IssueTemplateIncorrectFormat : IssueTemplate + { + public IssueTemplateIncorrectFormat(ICheck check) + : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}); Use mp3 or ogg for the song's audio.") + { + } + + public Issue Create(string file, string format) => new Issue(this, file, format); + } + } +} From c32d99250f1dce9d3bed129fd19d1ea7d9a02d22 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Tue, 16 Apr 2024 06:53:55 -0300 Subject: [PATCH 223/581] Deal with corrupt audio files This removes the corrupt file check from CheckTooShortAudioFiles and makes the audio formats checks deal with it instead to avoid redundant messages. --- .../Checks/CheckHitsoundsFormatTest.cs | 11 +++++++++ .../Editing/Checks/CheckSongFormatTest.cs | 12 ++++++++++ .../Checks/CheckTooShortAudioFilesTest.cs | 12 ---------- .../Edit/Checks/CheckHitsoundsFormat.cs | 2 +- .../Rulesets/Edit/Checks/CheckSongFormat.cs | 2 +- .../Edit/Checks/CheckTooShortAudioFiles.cs | 24 +------------------ 6 files changed, 26 insertions(+), 37 deletions(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs index 912a7468f5..f85a296c74 100644 --- a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs @@ -85,6 +85,17 @@ namespace osu.Game.Tests.Editing.Checks } } + [Test] + public void TestCorruptAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/corrupt.wav")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckHitsoundsFormat.IssueTemplateFormatUnsupported); + } + } + private BeatmapVerifierContext getContext(Stream? resourceStream) { var mockWorkingBeatmap = new Mock(beatmap, null, null); diff --git a/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs index acbf25ebad..0755fdd8ac 100644 --- a/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs @@ -89,6 +89,18 @@ namespace osu.Game.Tests.Editing.Checks } } + [Test] + public void TestCorruptAudio() + { + using (var resourceStream = TestResources.OpenResource("Samples/corrupt.wav")) + { + beatmap.Metadata.AudioFile = "abc123.mp3"; + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckSongFormat.IssueTemplateFormatUnsupported); + } + } + private BeatmapVerifierContext getContext(Stream? resourceStream) { var mockWorkingBeatmap = new Mock(beatmap, null, null); diff --git a/osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs b/osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs index 4918369460..b646e63955 100644 --- a/osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckTooShortAudioFilesTest.cs @@ -95,18 +95,6 @@ namespace osu.Game.Tests.Editing.Checks } } - [Test] - public void TestCorruptAudioFile() - { - using (var resourceStream = TestResources.OpenResource("Samples/corrupt.wav")) - { - var issues = check.Run(getContext(resourceStream)).ToList(); - - Assert.That(issues, Has.Count.EqualTo(1)); - Assert.That(issues.Single().Template is CheckTooShortAudioFiles.IssueTemplateBadFormat); - } - } - private BeatmapVerifierContext getContext(Stream? resourceStream) { var mockWorkingBeatmap = new Mock(beatmap, null, null); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index e490a23963..f65b89fc01 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateFormatUnsupported : IssueTemplate { public IssueTemplateFormatUnsupported(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" is using a unsupported format; Use wav or ogg for hitsounds.") + : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format; Use wav or ogg for hitsounds.") { } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs index 4162bf20a3..ae90dd96d5 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateFormatUnsupported : IssueTemplate { public IssueTemplateFormatUnsupported(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" is using a unsupported format; Use mp3 or ogg for the song's audio.") + : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format; Use mp3 or ogg for the song's audio.") { } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs b/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs index 32a3aa5ad9..3f85926e04 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs @@ -13,14 +13,12 @@ namespace osu.Game.Rulesets.Edit.Checks public class CheckTooShortAudioFiles : ICheck { private const int ms_threshold = 25; - private const int min_bytes_threshold = 100; public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Too short audio files"); public IEnumerable PossibleTemplates => new IssueTemplate[] { new IssueTemplateTooShort(this), - new IssueTemplateBadFormat(this) }; public IEnumerable Run(BeatmapVerifierContext context) @@ -39,15 +37,7 @@ namespace osu.Game.Rulesets.Edit.Checks var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Prescan, fileCallbacks.Callbacks, fileCallbacks.Handle); - if (decodeStream == 0) - { - // If the file is not likely to be properly parsed by Bass, we don't produce Error issues about it. - // Image files and audio files devoid of audio data both fail, for example, but neither would be issues in this check. - if (AudioCheckUtils.HasAudioExtension(file.Filename) && probablyHasAudioData(data)) - yield return new IssueTemplateBadFormat(this).Create(file.Filename); - - continue; - } + if (decodeStream == 0) continue; long length = Bass.ChannelGetLength(decodeStream); double ms = Bass.ChannelBytes2Seconds(decodeStream, length) * 1000; @@ -60,8 +50,6 @@ namespace osu.Game.Rulesets.Edit.Checks } } - private bool probablyHasAudioData(Stream data) => data.Length > min_bytes_threshold; - public class IssueTemplateTooShort : IssueTemplate { public IssueTemplateTooShort(ICheck check) @@ -71,15 +59,5 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, double ms) => new Issue(this, filename, ms, ms_threshold); } - - public class IssueTemplateBadFormat : IssueTemplate - { - public IssueTemplateBadFormat(ICheck check) - : base(check, IssueType.Error, "Could not check whether \"{0}\" is too short (code \"{1}\").") - { - } - - public Issue Create(string filename) => new Issue(this, filename, Bass.LastError); - } } } From c4bf03e6400d5047d2ebf916a7bd532334fff19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Apr 2024 12:42:12 +0200 Subject: [PATCH 224/581] Add failing test --- .../TestSceneResume.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneResume.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneResume.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneResume.cs new file mode 100644 index 0000000000..023016c32d --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneResume.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public partial class TestSceneResume : PlayerTestScene + { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, false, AllowBackwardsSeeks); + + [Test] + public void TestPauseViaKeyboard() + { + AddStep("move mouse to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + AddUntilStep("wait for gameplay start", () => Player.LocalUserPlaying.Value); + AddStep("press escape", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("wait for pause overlay", () => Player.ChildrenOfType().Single().State.Value, () => Is.EqualTo(Visibility.Visible)); + AddStep("release escape", () => InputManager.ReleaseKey(Key.Escape)); + AddStep("resume", () => + { + InputManager.Key(Key.Down); + InputManager.Key(Key.Space); + }); + AddUntilStep("pause overlay present", () => Player.DrawableRuleset.ResumeOverlay.State.Value, () => Is.EqualTo(Visibility.Visible)); + } + + [Test] + public void TestPauseViaKeyboardWhenMouseOutsidePlayfield() + { + AddStep("move mouse outside playfield", () => InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.BottomRight + new Vector2(1))); + AddUntilStep("wait for gameplay start", () => Player.LocalUserPlaying.Value); + AddStep("press escape", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("wait for pause overlay", () => Player.ChildrenOfType().Single().State.Value, () => Is.EqualTo(Visibility.Visible)); + AddStep("release escape", () => InputManager.ReleaseKey(Key.Escape)); + AddStep("resume", () => + { + InputManager.Key(Key.Down); + InputManager.Key(Key.Space); + }); + AddUntilStep("pause overlay present", () => Player.DrawableRuleset.ResumeOverlay.State.Value, () => Is.EqualTo(Visibility.Visible)); + } + + [Test] + public void TestPauseViaKeyboardWhenMouseOutsideScreen() + { + AddStep("move mouse outside playfield", () => InputManager.MoveMouseTo(new Vector2(-20))); + AddUntilStep("wait for gameplay start", () => Player.LocalUserPlaying.Value); + AddStep("press escape", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("wait for pause overlay", () => Player.ChildrenOfType().Single().State.Value, () => Is.EqualTo(Visibility.Visible)); + AddStep("release escape", () => InputManager.ReleaseKey(Key.Escape)); + AddStep("resume", () => + { + InputManager.Key(Key.Down); + InputManager.Key(Key.Space); + }); + AddUntilStep("pause overlay not present", () => Player.DrawableRuleset.ResumeOverlay.State.Value, () => Is.EqualTo(Visibility.Hidden)); + } + } +} From f9873968a5f06fa355b074f834fad8e9579a3ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Apr 2024 13:37:12 +0200 Subject: [PATCH 225/581] Apply NRT in `OsuResumeOverlay` --- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index adc7bd97ff..19d8a94f0a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -19,12 +17,12 @@ namespace osu.Game.Rulesets.Osu.UI { public partial class OsuResumeOverlay : ResumeOverlay { - private Container cursorScaleContainer; - private OsuClickToResumeCursor clickToResumeCursor; + private Container cursorScaleContainer = null!; + private OsuClickToResumeCursor clickToResumeCursor = null!; - private OsuCursorContainer localCursorContainer; + private OsuCursorContainer? localCursorContainer; - public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null; + public override CursorContainer? LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null; protected override LocalisableString Message => "Click the orange cursor to resume"; @@ -71,8 +69,8 @@ namespace osu.Game.Rulesets.Osu.UI { public override bool HandlePositionalInput => true; - public Action ResumeRequested; - private Container scaleTransitionContainer; + public Action? ResumeRequested; + private Container scaleTransitionContainer = null!; public OsuClickToResumeCursor() { From e5e345712efea6467ae895219cad53a461403672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Apr 2024 13:24:30 +0200 Subject: [PATCH 226/581] Fix resume overlay not appearing after pausing inside window but outside of actual playfield area Related to https://github.com/ppy/osu/discussions/27871 (although does not actually fix the issue with the pause button, _if_ it is to be considered an issue - the problem there is that the gameplay cursor gets hidden, so the other condition in the modified check takes over). Regressed in https://github.com/ppy/osu/commit/bce3bd55e5a863e52f41598c306a248a79638843. Reasoning for breakage is silent change in `this` when moving the `Contains()` check (`DrawableRuleset` will encompass screen bounds, while `OsuResumeOverlay` is only as big as the actual playfield). --- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index 19d8a94f0a..a04ea80640 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osuTK.Graphics; @@ -26,6 +27,9 @@ namespace osu.Game.Rulesets.Osu.UI protected override LocalisableString Message => "Click the orange cursor to resume"; + [Resolved] + private DrawableRuleset? drawableRuleset { get; set; } + [BackgroundDependencyLoader] private void load() { @@ -38,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override void PopIn() { // Can't display if the cursor is outside the window. - if (GameplayCursor.LastFrameState == Visibility.Hidden || !Contains(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre)) + if (GameplayCursor.LastFrameState == Visibility.Hidden || drawableRuleset?.Contains(GameplayCursor.ActiveCursor.ScreenSpaceDrawQuad.Centre) == false) { Resume(); return; From 6c943681b0518848b5ace9755835e16996c505aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Apr 2024 16:07:56 +0200 Subject: [PATCH 227/581] Fix preview tracks playing after their owning overlay has hidden RFC. Closes https://github.com/ppy/osu/issues/27883. The idea here is that `PopOut()` is called _when the hide is requested_, so once an overlay trigger would hide, the overlay would `StopAnyPlaying()`, but because of async load things, the actual track would start playing after that but before the overlay has fully hidden. (That last part is significant because after the overlay has fully hidden, schedules save the day.) Due to the loose coupling between `PreviewTrackManager` and `IPreviewTrackOwner` there's really no easy way to handle this locally to the usages of the preview tracks. Heck, `PreviewTrackManager` doesn't really know which preview track owner is to be considered _present_ at any time, it just kinda works on vibes based on DI until the owner tells all of its preview tracks to stop. This solution causes the preview tracks to stop a little bit later but maybe that's fine? Just trying to not overthink the issue is all. No tests because this is going to suck to test automatically while it is pretty easy to test manually (got it in a few tries on master). The issue also mentions that the track can sometimes resume playing after the overlay is pulled up again, but I don't see that as a problem necessarily, and even if it was, it's not going to be that easy to address due to the aforementioned loose coupling - to fix that, play buttons would have to know who is the current preview track owner and cancel schedules upon determining that their preview track owner has gone away. --- osu.Game/Overlays/WaveOverlayContainer.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 0295ff467a..7744db5dd5 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -40,10 +40,12 @@ namespace osu.Game.Overlays protected override void PopOut() { - base.PopOut(); - Waves.Hide(); - this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InQuint); + this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InQuint) + // base call is responsible for stopping preview tracks. + // delay it until the fade has concluded to ensure that nothing inside the overlay has triggered + // another preview track playback in the meantime, leaving an "orphaned" preview playing. + .OnComplete(_ => base.PopOut()); } } } From a386068ed3cdad2b7f3009780c7e6f7709778119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 08:51:53 +0200 Subject: [PATCH 228/581] Add `ScoreInfo.TotalScoreWithoutMods` --- osu.Game/Database/RealmAccess.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 167d170c81..12a71f3510 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -91,8 +91,9 @@ namespace osu.Game.Database /// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo. /// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values. /// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on. + /// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances. /// - private const int schema_version = 40; + private const int schema_version = 41; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index fd98107792..92c18c9c1e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -65,8 +65,19 @@ namespace osu.Game.Scoring public bool DeletePending { get; set; } + /// + /// The total number of points awarded for the score. + /// public long TotalScore { get; set; } + /// + /// The total number of points awarded for the score without including mod multipliers. + /// + /// + /// The purpose of this property is to enable future lossless rebalances of mod multipliers. + /// + public long TotalScoreWithoutMods { get; set; } + /// /// The version of processing applied to calculate total score as stored in the database. /// If this does not match , From 6b0d577f0bb3943cc66cd5bff118b52315ea7418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 08:52:27 +0200 Subject: [PATCH 229/581] Add backmigration of `TotalScoreWithoutMods` for existing scores --- osu.Game/Database/RealmAccess.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 12a71f3510..ee8eb4491b 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1109,6 +1109,19 @@ namespace osu.Game.Database } } + break; + + case 41: + foreach (var score in migration.NewRealm.All()) + { + double modMultiplier = 1; + + foreach (var mod in score.Mods) + modMultiplier *= mod.ScoreMultiplier; + + score.TotalScoreWithoutMods = (long)Math.Round(score.TotalScore / modMultiplier); + } + break; } From e0178802b81f5a62ad0ddb8ed02cab7d98d5ebd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 08:52:47 +0200 Subject: [PATCH 230/581] Populate `TotalScoreWithoutMods` on scores set locally --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 9d12daad04..70d7f0fe37 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -56,6 +56,14 @@ namespace osu.Game.Rulesets.Scoring /// public readonly BindableLong TotalScore = new BindableLong { MinValue = 0 }; + /// + /// The total number of points awarded for the score without including mod multipliers. + /// + /// + /// The purpose of this property is to enable future lossless rebalances of mod multipliers. + /// + public readonly BindableLong TotalScoreWithoutMods = new BindableLong { MinValue = 0 }; + /// /// The current accuracy. /// @@ -363,7 +371,8 @@ namespace osu.Game.Rulesets.Scoring double comboProgress = maximumComboPortion > 0 ? currentComboPortion / maximumComboPortion : 1; double accuracyProcess = maximumAccuracyJudgementCount > 0 ? (double)currentAccuracyJudgementCount / maximumAccuracyJudgementCount : 1; - TotalScore.Value = (long)Math.Round(ComputeTotalScore(comboProgress, accuracyProcess, currentBonusPortion) * scoreMultiplier); + TotalScoreWithoutMods.Value = (long)Math.Round(ComputeTotalScore(comboProgress, accuracyProcess, currentBonusPortion)); + TotalScore.Value = (long)Math.Round(TotalScoreWithoutMods.Value * scoreMultiplier); } private void updateRank() @@ -446,6 +455,7 @@ namespace osu.Game.Rulesets.Scoring score.MaximumStatistics[result] = MaximumResultCounts.GetValueOrDefault(result); // Populate total score after everything else. + score.TotalScoreWithoutMods = TotalScoreWithoutMods.Value; score.TotalScore = TotalScore.Value; } From 2f1a4cdaa4d46c0dfd230f618c57563cee6bb654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 09:08:15 +0200 Subject: [PATCH 231/581] Export and import `TotalScoreWithoutMods` to replays (or recalculate if it missing) --- .../Formats/LegacyScoreDecoderTest.cs | 74 +++++++++++++++++++ osu.Game/Database/RealmAccess.cs | 9 +-- .../Legacy/LegacyReplaySoloScoreInfo.cs | 4 + osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 15 ++++ 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index 43e471320e..2f8cb9a3b3 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -412,6 +412,80 @@ namespace osu.Game.Tests.Beatmaps.Formats }); } + [Test] + public void TestTotalScoreWithoutModsReadIfPresent() + { + var ruleset = new OsuRuleset().RulesetInfo; + + var scoreInfo = TestResources.CreateTestScoreInfo(ruleset); + scoreInfo.Mods = new Mod[] + { + new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } + }; + scoreInfo.OnlineID = 123123; + scoreInfo.ClientVersion = "2023.1221.0"; + scoreInfo.TotalScoreWithoutMods = 1_000_000; + scoreInfo.TotalScore = 1_020_000; + + var beatmap = new TestBeatmap(ruleset); + var score = new Score + { + ScoreInfo = scoreInfo, + Replay = new Replay + { + Frames = new List + { + new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton) + } + } + }; + + var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap); + + Assert.Multiple(() => + { + Assert.That(decodedAfterEncode.ScoreInfo.TotalScoreWithoutMods, Is.EqualTo(1_000_000)); + Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(1_020_000)); + }); + } + + [Test] + public void TestTotalScoreWithoutModsBackwardsPopulatedIfMissing() + { + var ruleset = new OsuRuleset().RulesetInfo; + + var scoreInfo = TestResources.CreateTestScoreInfo(ruleset); + scoreInfo.Mods = new Mod[] + { + new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } + }; + scoreInfo.OnlineID = 123123; + scoreInfo.ClientVersion = "2023.1221.0"; + scoreInfo.TotalScoreWithoutMods = 0; + scoreInfo.TotalScore = 1_020_000; + + var beatmap = new TestBeatmap(ruleset); + var score = new Score + { + ScoreInfo = scoreInfo, + Replay = new Replay + { + Frames = new List + { + new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton) + } + } + }; + + var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap); + + Assert.Multiple(() => + { + Assert.That(decodedAfterEncode.ScoreInfo.TotalScoreWithoutMods, Is.EqualTo(1_000_000)); + Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(1_020_000)); + }); + } + private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap) { var encodeStream = new MemoryStream(); diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index ee8eb4491b..c075c3d82b 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1113,14 +1113,7 @@ namespace osu.Game.Database case 41: foreach (var score in migration.NewRealm.All()) - { - double modMultiplier = 1; - - foreach (var mod in score.Mods) - modMultiplier *= mod.ScoreMultiplier; - - score.TotalScoreWithoutMods = (long)Math.Round(score.TotalScore / modMultiplier); - } + LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score); break; } diff --git a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs index afdcef1d21..c11e18462a 100644 --- a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs @@ -38,6 +38,9 @@ namespace osu.Game.Scoring.Legacy [JsonProperty("client_version")] public string ClientVersion = string.Empty; + [JsonProperty("total_score_without_mods")] + public long? TotalScoreWithoutMods { get; set; } + public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo { OnlineID = score.OnlineID, @@ -45,6 +48,7 @@ namespace osu.Game.Scoring.Legacy Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(), MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(), ClientVersion = score.ClientVersion, + TotalScoreWithoutMods = score.TotalScoreWithoutMods > 0 ? score.TotalScoreWithoutMods : null, }; } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 65e2c02655..2358c0dfec 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -129,6 +129,11 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics; score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray(); score.ScoreInfo.ClientVersion = readScore.ClientVersion; + + if (readScore.TotalScoreWithoutMods is long totalScoreWithoutMods) + score.ScoreInfo.TotalScoreWithoutMods = totalScoreWithoutMods; + else + PopulateTotalScoreWithoutMods(score.ScoreInfo); }); } } @@ -237,6 +242,16 @@ namespace osu.Game.Scoring.Legacy #pragma warning restore CS0618 } + public static void PopulateTotalScoreWithoutMods(ScoreInfo score) + { + double modMultiplier = 1; + + foreach (var mod in score.Mods) + modMultiplier *= mod.ScoreMultiplier; + + score.TotalScoreWithoutMods = (long)Math.Round(score.TotalScore / modMultiplier); + } + private void readLegacyReplay(Replay replay, StreamReader reader) { float lastTime = beatmapOffset; From 18bb81e7a7e4de6cf15ce0674ae44ef9f041ff84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 09:11:47 +0200 Subject: [PATCH 232/581] Populate total score without mods when performing standardised score migration --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 6f2f8d64fa..7d09ebdb40 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Scoring; +using osu.Game.Scoring.Legacy; namespace osu.Game.Database { @@ -248,6 +249,7 @@ namespace osu.Game.Database score.Accuracy = computeAccuracy(score, scoreProcessor); score.Rank = computeRank(score, scoreProcessor); score.TotalScore = convertFromLegacyTotalScore(score, ruleset, beatmap); + LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score); } /// From e11e9fe14ff41af53eb6ccbbf9eb79952d045989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Apr 2024 09:15:50 +0200 Subject: [PATCH 233/581] Add `TotalScoreWithoutMods` to `SoloScoreInfo` End goal being storing it server-side. --- osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index 64caddb2fc..36f1311f9d 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -33,6 +33,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("total_score")] public long TotalScore { get; set; } + [JsonProperty("total_score_without_mods")] + public long TotalScoreWithoutMods { get; set; } + [JsonProperty("accuracy")] public double Accuracy { get; set; } @@ -206,6 +209,7 @@ namespace osu.Game.Online.API.Requests.Responses Ruleset = new RulesetInfo { OnlineID = RulesetID }, Passed = Passed, TotalScore = TotalScore, + TotalScoreWithoutMods = TotalScoreWithoutMods, LegacyTotalScore = LegacyTotalScore, Accuracy = Accuracy, MaxCombo = MaxCombo, @@ -239,6 +243,7 @@ namespace osu.Game.Online.API.Requests.Responses { Rank = score.Rank, TotalScore = score.TotalScore, + TotalScoreWithoutMods = score.TotalScoreWithoutMods, Accuracy = score.Accuracy, PP = score.PP, MaxCombo = score.MaxCombo, From 03e13ddc3095e8713a90dab9bb5f672b0ae75d4d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Apr 2024 17:10:19 +0900 Subject: [PATCH 234/581] Globally silence Discord RPC registration failures --- osu.Desktop/DiscordRichPresence.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index f1c796d0cd..74ebd38f2c 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -6,7 +6,6 @@ using System.Text; using DiscordRPC; using DiscordRPC.Message; using Newtonsoft.Json; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; @@ -80,14 +79,20 @@ namespace osu.Desktop client.OnReady += onReady; client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Message} ({e.Code})", LoggingTarget.Network, LogLevel.Error); - // A URI scheme is required to support game invitations, as well as informing Discord of the game executable path to support launching the game when a user clicks on join/spectate. - // The library doesn't properly support URI registration when ran from an app bundle on macOS. - if (!RuntimeInfo.IsApple) + try { client.RegisterUriScheme(); client.Subscribe(EventType.Join); client.OnJoin += onJoin; } + catch (Exception ex) + { + // This is known to fail in at least the following sandboxed environments: + // - macOS (when packaged as an app bundle) + // - flatpak (see: https://github.com/flathub/sh.ppy.osu/issues/170) + // There is currently no better way to do this offered by Discord, so the best we can do is simply ignore it for now. + Logger.Log($"Failed to register Discord URI scheme: {ex}"); + } client.Initialize(); } From 2a3ae6bce178124e65ca14d8ea62e82a6aa05ded Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 02:34:41 +0300 Subject: [PATCH 235/581] Update all `TabItem` implementations to play select sample on `OnActivatedByUser` --- .../Graphics/UserInterface/OsuTabControl.cs | 24 ++++++++++++------- .../Graphics/UserInterface/PageTabControl.cs | 14 ++++++++++- .../BeatmapListingCardSizeTabControl.cs | 12 ++++++++-- .../Overlays/BeatmapListing/FilterTabItem.cs | 12 ++++++++-- .../OverlayPanelDisplayStyleControl.cs | 14 ++++++++++- osu.Game/Overlays/OverlayRulesetTabItem.cs | 14 ++++++++++- osu.Game/Overlays/OverlayStreamItem.cs | 12 ++++++++-- osu.Game/Overlays/OverlayTabControl.cs | 14 ++++++++++- .../Toolbar/ToolbarRulesetSelector.cs | 4 ---- .../Toolbar/ToolbarRulesetTabButton.cs | 12 ++++++++++ .../Match/Components/MatchTypePicker.cs | 11 +++++++-- 11 files changed, 119 insertions(+), 24 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index f24977927f..5ce384c53c 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -8,6 +8,8 @@ using System.Linq; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; @@ -143,13 +145,6 @@ namespace osu.Game.Graphics.UserInterface FadeUnhovered(); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - if (accentColour == default) - AccentColour = colours.Blue; - } - public OsuTabItem(T value) : base(value) { @@ -196,10 +191,21 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, }, - new HoverClickSounds(HoverSampleSet.TabSelect) + new HoverSounds(HoverSampleSet.TabSelect) }; } + private Sample selectSample; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + if (accentColour == default) + AccentColour = colours.Blue; + + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + } + protected override void OnActivated() { Text.Font = Text.Font.With(weight: FontWeight.Bold); @@ -211,6 +217,8 @@ namespace osu.Game.Graphics.UserInterface Text.Font = Text.Font.With(weight: FontWeight.Medium); FadeUnhovered(); } + + protected override void OnActivatedByUser() => selectSample.Play(); } } } diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index 2fe8acfbd5..44c659f945 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -7,6 +7,8 @@ using System; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; @@ -53,6 +55,8 @@ namespace osu.Game.Graphics.UserInterface } } + private Sample selectSample = null!; + public PageTabItem(T value) : base(value) { @@ -78,12 +82,18 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, }, - new HoverClickSounds(HoverSampleSet.TabSelect) + new HoverSounds(HoverSampleSet.TabSelect) }; Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true); } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + } + protected virtual LocalisableString CreateText() => (Value as Enum)?.GetLocalisableDescription() ?? Value.ToString(); protected override bool OnHover(HoverEvent e) @@ -112,6 +122,8 @@ namespace osu.Game.Graphics.UserInterface protected override void OnActivated() => slideActive(); protected override void OnDeactivated() => slideInactive(); + + protected override void OnActivatedByUser() => selectSample.Play(); } } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs index 9cd0031e3d..63a533c92e 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingCardSizeTabControl.cs @@ -5,6 +5,8 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -47,13 +49,15 @@ namespace osu.Game.Overlays.BeatmapListing [Resolved] private OverlayColourProvider colourProvider { get; set; } + private Sample selectSample = null!; + public TabItem(BeatmapCardSize value) : base(value) { } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { AutoSizeAxes = Axes.Both; Masking = true; @@ -79,8 +83,10 @@ namespace osu.Game.Overlays.BeatmapListing Icon = getIconForCardSize(Value) } }, - new HoverClickSounds(HoverSampleSet.TabSelect) + new HoverSounds(HoverSampleSet.TabSelect) }; + + selectSample = audio.Samples.Get(@"UI/tabselect-select"); } private static IconUsage getIconForCardSize(BeatmapCardSize cardSize) @@ -111,6 +117,8 @@ namespace osu.Game.Overlays.BeatmapListing updateState(); } + protected override void OnActivatedByUser() => selectSample.Play(); + protected override void OnDeactivated() { if (IsLoaded) diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs index 831cf812ff..89bf61dd18 100644 --- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs +++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs @@ -5,6 +5,8 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; @@ -24,13 +26,15 @@ namespace osu.Game.Overlays.BeatmapListing private OsuSpriteText text; + private Sample selectSample = null!; + public FilterTabItem(T value) : base(value) { } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { AutoSizeAxes = Axes.Both; AddRangeInternal(new Drawable[] @@ -40,10 +44,12 @@ namespace osu.Game.Overlays.BeatmapListing Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular), Text = LabelFor(Value) }, - new HoverClickSounds(HoverSampleSet.TabSelect) + new HoverSounds(HoverSampleSet.TabSelect) }); Enabled.Value = true; + + selectSample = audio.Samples.Get(@"UI/tabselect-select"); } protected override void LoadComplete() @@ -71,6 +77,8 @@ namespace osu.Game.Overlays.BeatmapListing protected override void OnDeactivated() => UpdateState(); + protected override void OnActivatedByUser() => selectSample.Play(); + /// /// Returns the label text to be used for the supplied . /// diff --git a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs index d7d6bd4a2a..c2bea0ed91 100644 --- a/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs +++ b/osu.Game/Overlays/OverlayPanelDisplayStyleControl.cs @@ -11,6 +11,8 @@ using osuTK; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osuTK.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; @@ -65,6 +67,8 @@ namespace osu.Game.Overlays private readonly SpriteIcon icon; + private Sample selectSample = null!; + public PanelDisplayTabItem(OverlayPanelDisplayStyle value) : base(value) { @@ -78,14 +82,22 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit }, - new HoverClickSounds() + new HoverSounds(HoverSampleSet.TabSelect) }); } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + } + protected override void OnActivated() => updateState(); protected override void OnDeactivated() => updateState(); + protected override void OnActivatedByUser() => selectSample.Play(); + protected override bool OnHover(HoverEvent e) { updateState(); diff --git a/osu.Game/Overlays/OverlayRulesetTabItem.cs b/osu.Game/Overlays/OverlayRulesetTabItem.cs index 6d318820b3..b245486adf 100644 --- a/osu.Game/Overlays/OverlayRulesetTabItem.cs +++ b/osu.Game/Overlays/OverlayRulesetTabItem.cs @@ -10,6 +10,8 @@ using osu.Game.Rulesets; using osuTK.Graphics; using osuTK; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; @@ -39,6 +41,8 @@ namespace osu.Game.Overlays public LocalisableString TooltipText => Value.Name; + private Sample selectSample = null!; + public OverlayRulesetTabItem(RulesetInfo value) : base(value) { @@ -59,12 +63,18 @@ namespace osu.Game.Overlays Icon = value.CreateInstance().CreateIcon(), }, }, - new HoverClickSounds() + new HoverSounds(HoverSampleSet.TabSelect) }); Enabled.Value = true; } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -90,6 +100,8 @@ namespace osu.Game.Overlays protected override void OnDeactivated() => updateState(); + protected override void OnActivatedByUser() => selectSample.Play(); + private void updateState() { AccentColour = Enabled.Value ? getActiveColour() : colourProvider.Foreground1; diff --git a/osu.Game/Overlays/OverlayStreamItem.cs b/osu.Game/Overlays/OverlayStreamItem.cs index 45181c13e4..f0ae0b41fc 100644 --- a/osu.Game/Overlays/OverlayStreamItem.cs +++ b/osu.Game/Overlays/OverlayStreamItem.cs @@ -11,6 +11,8 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Sprites; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Game.Graphics.Sprites; using osu.Game.Graphics; using osuTK.Graphics; @@ -49,8 +51,10 @@ namespace osu.Game.Overlays Margin = new MarginPadding(PADDING); } + private Sample selectSample; + [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuColour colours) + private void load(OverlayColourProvider colourProvider, OsuColour colours, AudioManager audio) { AddRange(new Drawable[] { @@ -87,9 +91,11 @@ namespace osu.Game.Overlays CollapsedSize = 2, Expanded = true }, - new HoverClickSounds() + new HoverSounds(HoverSampleSet.TabSelect) }); + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + SelectedItem.BindValueChanged(_ => updateState(), true); } @@ -105,6 +111,8 @@ namespace osu.Game.Overlays protected override void OnDeactivated() => updateState(); + protected override void OnActivatedByUser() => selectSample.Play(); + protected override bool OnHover(HoverEvent e) { updateState(); diff --git a/osu.Game/Overlays/OverlayTabControl.cs b/osu.Game/Overlays/OverlayTabControl.cs index 884e31868f..a27caa13f1 100644 --- a/osu.Game/Overlays/OverlayTabControl.cs +++ b/osu.Game/Overlays/OverlayTabControl.cs @@ -4,6 +4,8 @@ #nullable disable using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; @@ -80,6 +82,8 @@ namespace osu.Game.Overlays } } + private Sample selectSample = null!; + public OverlayTabItem(T value) : base(value) { @@ -101,10 +105,16 @@ namespace osu.Game.Overlays ExpandedSize = 5f, CollapsedSize = 0 }, - new HoverClickSounds(HoverSampleSet.TabSelect) + new HoverSounds(HoverSampleSet.TabSelect) }; } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + selectSample = audio.Samples.Get(@"UI/tabselect-select"); + } + protected override bool OnHover(HoverEvent e) { base.OnHover(e); @@ -136,6 +146,8 @@ namespace osu.Game.Overlays Text.Font = Text.Font.With(weight: FontWeight.Medium); } + protected override void OnActivatedByUser() => selectSample.Play(); + private void updateState() { if (Active.Value) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 723c24597a..518152e455 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -88,10 +88,6 @@ namespace osu.Game.Overlays.Toolbar if (SelectedTab != null) { ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 500, Easing.OutElasticQuarter); - - if (hasInitialPosition) - selectionSamples[SelectedTab.Value.ShortName]?.Play(); - hasInitialPosition = true; } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index 3287ac6eaa..0315bede64 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; @@ -17,6 +19,8 @@ namespace osu.Game.Overlays.Toolbar { private readonly RulesetButton ruleset; + private Sample? selectSample; + public ToolbarRulesetTabButton(RulesetInfo value) : base(value) { @@ -34,10 +38,18 @@ namespace osu.Game.Overlays.Toolbar ruleset.SetIcon(rInstance.CreateIcon()); } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + selectSample = audio.Samples.Get($@"UI/ruleset-select-{Value.ShortName}"); + } + protected override void OnActivated() => ruleset.Active = true; protected override void OnDeactivated() => ruleset.Active = false; + protected override void OnActivatedByUser() => selectSample?.Play(); + private partial class RulesetButton : ToolbarButton { protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs index 995fce085e..477336e8ea 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs @@ -4,6 +4,8 @@ #nullable disable using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -78,14 +80,17 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components }, }, }, - new HoverClickSounds(), + new HoverSounds(HoverSampleSet.TabSelect), }; } + private Sample selectSample; + [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { selection.Colour = colours.Yellow; + selectSample = audio.Samples.Get(@"UI/tabselect-select"); } protected override bool OnHover(HoverEvent e) @@ -109,6 +114,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components { selection.FadeOut(transition_duration, Easing.OutQuint); } + + protected override void OnActivatedByUser() => selectSample.Play(); } } } From 50a21fb7273489f682b680877ad69200a2d4c227 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 02:38:39 +0300 Subject: [PATCH 236/581] Change `ToolbarRulesetSelector` to use `SelectItem` to trigger new sound feedback path --- osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 518152e455..d49c340ed4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -3,11 +3,8 @@ #nullable disable -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -24,8 +21,6 @@ namespace osu.Game.Overlays.Toolbar { protected Drawable ModeButtonLine { get; private set; } - private readonly Dictionary selectionSamples = new Dictionary(); - public ToolbarRulesetSelector() { RelativeSizeAxes = Axes.Y; @@ -33,7 +28,7 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load() { AddRangeInternal(new[] { @@ -59,9 +54,6 @@ namespace osu.Game.Overlays.Toolbar } }, }); - - foreach (var ruleset in Rulesets.AvailableRulesets) - selectionSamples[ruleset.ShortName] = audio.Samples.Get($"UI/ruleset-select-{ruleset.ShortName}"); } protected override void LoadComplete() @@ -117,7 +109,7 @@ namespace osu.Game.Overlays.Toolbar RulesetInfo found = Rulesets.AvailableRulesets.ElementAtOrDefault(requested); if (found != null) - Current.Value = found; + SelectItem(found); return true; } From 42892acc1adaf8be01897de0c4e5fef7b6319a4d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 02:42:24 +0300 Subject: [PATCH 237/581] Fix multiple selection filter tab items no longer playing sounds These tab items are not managed by a `TabControl`, activation events are manually served by the class itself toggling `Active` when clicked. --- .../BeatmapSearchMultipleSelectionFilterRow.cs | 1 + osu.Game/Overlays/BeatmapListing/FilterTabItem.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs index e59beb43ff..0a4c2b1e21 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs @@ -129,6 +129,7 @@ namespace osu.Game.Overlays.BeatmapListing { base.OnClick(e); Active.Toggle(); + SelectSample.Play(); return true; } } diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs index 89bf61dd18..ee188d34ce 100644 --- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs +++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapListing private OsuSpriteText text; - private Sample selectSample = null!; + protected Sample SelectSample { get; private set; } = null!; public FilterTabItem(T value) : base(value) @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.BeatmapListing Enabled.Value = true; - selectSample = audio.Samples.Get(@"UI/tabselect-select"); + SelectSample = audio.Samples.Get(@"UI/tabselect-select"); } protected override void LoadComplete() @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.BeatmapListing protected override void OnDeactivated() => UpdateState(); - protected override void OnActivatedByUser() => selectSample.Play(); + protected override void OnActivatedByUser() => SelectSample.Play(); /// /// Returns the label text to be used for the supplied . From 9e692686760ea1f00cf661bc011da18e8b4b43a0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 02:48:32 +0300 Subject: [PATCH 238/581] Change editor screen selection logic to use `SelectItem` for sound feedback --- osu.Game/Screens/Edit/Editor.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c1f6c02301..37f4b4f5be 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -372,7 +372,7 @@ namespace osu.Game.Screens.Edit } } }, - new EditorScreenSwitcherControl + screenSwitcher = new EditorScreenSwitcherControl { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -662,23 +662,23 @@ namespace osu.Game.Screens.Edit return true; case GlobalAction.EditorComposeMode: - Mode.Value = EditorScreenMode.Compose; + screenSwitcher.SelectItem(EditorScreenMode.Compose); return true; case GlobalAction.EditorDesignMode: - Mode.Value = EditorScreenMode.Design; + screenSwitcher.SelectItem(EditorScreenMode.Design); return true; case GlobalAction.EditorTimingMode: - Mode.Value = EditorScreenMode.Timing; + screenSwitcher.SelectItem(EditorScreenMode.Timing); return true; case GlobalAction.EditorSetupMode: - Mode.Value = EditorScreenMode.SongSetup; + screenSwitcher.SelectItem(EditorScreenMode.SongSetup); return true; case GlobalAction.EditorVerifyMode: - Mode.Value = EditorScreenMode.Verify; + screenSwitcher.SelectItem(EditorScreenMode.Verify); return true; case GlobalAction.EditorTestGameplay: @@ -959,6 +959,8 @@ namespace osu.Game.Screens.Edit [CanBeNull] private ScheduledDelegate playbackDisabledDebounce; + private EditorScreenSwitcherControl screenSwitcher; + private void updateSampleDisabledState() { bool shouldDisableSamples = clock.SeekingOrStopped.Value From 24e8b88320af93d257674452178e878253ccb4fc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 03:00:27 +0300 Subject: [PATCH 239/581] Add inline comment to the custom implementation of multiple-selection tab items --- .../BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs index 0a4c2b1e21..4bd25f6561 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs @@ -128,6 +128,9 @@ namespace osu.Game.Overlays.BeatmapListing protected override bool OnClick(ClickEvent e) { base.OnClick(e); + + // this tab item implementation is not managed by a TabControl, + // therefore we have to manually update Active state and play select sound when this tab item is clicked. Active.Toggle(); SelectSample.Play(); return true; From a7e043bdbe3b145b5e33c392e06061e9e71fc7b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Apr 2024 03:08:29 +0300 Subject: [PATCH 240/581] Fix beatmap rulest selector playing sound for initial ruleset selection --- osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index 426fbcdb8d..29744f27fc 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet // propagate value to tab items first to enable only available rulesets. beatmapSet.Value = value; - SelectTab(TabContainer.TabItems.FirstOrDefault(t => t.Enabled.Value)); + Current.Value = TabContainer.TabItems.FirstOrDefault(t => t.Enabled.Value)?.Value; } } From 8d94f8d995cd34c2f9d7d6ab68a169b7e554c0f4 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 17 Apr 2024 08:14:19 -0300 Subject: [PATCH 241/581] Remove `BassFlags.Prescan` and free the decodeStream --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 7 ++++--- osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index f65b89fc01..c71c7b0ef3 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Edit.Checks continue; var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); - int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Prescan, fileCallbacks.Callbacks, fileCallbacks.Handle); + int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode, fileCallbacks.Callbacks, fileCallbacks.Handle); // If the format is not supported by BASS if (decodeStream == 0) @@ -61,9 +61,10 @@ namespace osu.Game.Rulesets.Edit.Checks var audioInfo = Bass.ChannelGetInfo(decodeStream); if (!allowedFormats.Contains(audioInfo.ChannelType)) - { yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename, audioInfo.ChannelType.ToString()); - } + + + Bass.StreamFree(decodeStream); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs index ae90dd96d5..9ed1350f56 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (data == null || data.Length <= 0) yield break; var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); - int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode | BassFlags.Prescan, fileCallbacks.Callbacks, fileCallbacks.Handle); + int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode, fileCallbacks.Callbacks, fileCallbacks.Handle); // If the format is not supported by BASS if (decodeStream == 0) @@ -55,6 +55,8 @@ namespace osu.Game.Rulesets.Edit.Checks if (!allowedFormats.Contains(audioInfo.ChannelType)) yield return new IssueTemplateIncorrectFormat(this).Create(audioFile.Filename, audioInfo.ChannelType.ToString()); + + Bass.StreamFree(decodeStream); } } From ac03856ebdb8f8ffff860879263f16ab2564756b Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Wed, 17 Apr 2024 08:22:05 -0300 Subject: [PATCH 242/581] Fix test methods names --- osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs | 8 ++++---- osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs | 8 ++++---- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs index f85a296c74..9a806f6cb7 100644 --- a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestMP3Audio() + public void TestMp3Audio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample-cut.mp3")) { @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestOGGAudio() + public void TestOggAudio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample.ogg")) { @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestWAVAudio() + public void TestWavAudio() { using (var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav")) { @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestWEBMAudio() + public void TestWebmAudio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample.webm")) { diff --git a/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs index 0755fdd8ac..98a4e1f9e9 100644 --- a/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckSongFormatTest.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestMP3Audio() + public void TestMp3Audio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample-cut.mp3")) { @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestOGGAudio() + public void TestOggAudio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample.ogg")) { @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestWAVAudio() + public void TestWavAudio() { using (var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav")) { @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Editing.Checks } [Test] - public void TestWEBMAudio() + public void TestWebmAudio() { using (var resourceStream = TestResources.OpenResource("Samples/test-sample.webm")) { diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index c71c7b0ef3..3343d07f25 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -63,7 +63,6 @@ namespace osu.Game.Rulesets.Edit.Checks if (!allowedFormats.Contains(audioInfo.ChannelType)) yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename, audioInfo.ChannelType.ToString()); - Bass.StreamFree(decodeStream); } } From f92833b588577f6e55f53948f97a8ed284867fb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Apr 2024 12:02:49 +0800 Subject: [PATCH 243/581] Add ability to quick exit from results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e579c3fe51..f28b9b2554 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -191,6 +191,16 @@ namespace osu.Game.Screens.Ranking }); } + AddInternal(new HotkeyExitOverlay + { + Action = () => + { + if (!this.IsCurrentScreen()) return; + + this.Exit(); + }, + }); + if (player != null && AllowRetry) { buttons.Add(new RetryButton { Width = 300 }); From d08b1e5ae7a413c3ec6a958b1bc592e20baf6c8e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 17 Apr 2024 21:18:54 -0700 Subject: [PATCH 244/581] Add xmldoc to `GetScore()` and `Export()` and remove xmldoc from private `getDatabasedScoreInfo()` --- osu.Game/Scoring/ScoreManager.cs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 3f6c6ee49d..b6bb637537 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -58,6 +58,15 @@ namespace osu.Game.Scoring }; } + /// + /// Retrieve a from a given . + /// + /// The to convert. + /// The . Null if the score on the database cannot be found. + /// + /// The is re-retrieved from the database to ensure all the required data + /// for retrieving a replay are present (may have missing properties if it was retrieved from online data). + /// public Score? GetScore(IScoreInfo scoreInfo) { ScoreInfo? databasedScoreInfo = getDatabasedScoreInfo(scoreInfo); @@ -75,12 +84,6 @@ namespace osu.Game.Scoring return Realm.Run(r => r.All().FirstOrDefault(query)?.Detach()); } - /// - /// Re-retrieve a given from the database to ensure all the required data for presenting / exporting a replay are present. - /// - /// The to attempt querying on. - /// The databased score info. Null if the score on the database cannot be found. - /// Can be used when the was retrieved from online data, as it may have missing properties. private ScoreInfo? getDatabasedScoreInfo(IScoreInfo originalScoreInfo) { ScoreInfo? databasedScoreInfo = null; @@ -194,6 +197,15 @@ namespace osu.Game.Scoring public Task>> Import(ProgressNotification notification, ImportTask[] tasks, ImportParameters parameters = default) => scoreImporter.Import(notification, tasks); + /// + /// Export a replay from a given . + /// + /// The to export. + /// The . Null if the score on the database cannot be found. + /// + /// The is re-retrieved from the database to ensure all the required data + /// for exporting a replay are present (may have missing properties if it was retrieved from online data). + /// public Task? Export(ScoreInfo scoreInfo) { ScoreInfo? databasedScoreInfo = getDatabasedScoreInfo(scoreInfo); From 46e2cfdba526d77106d1eff41ec5e542ac7e8bca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Apr 2024 18:33:30 +0800 Subject: [PATCH 245/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2d7a9d2652..bf02a5d8e2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index b2e3fc0779..4f973dbeb9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From a41ae99dd7e8f39c909a3519cce1817469b139a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Apr 2024 15:14:52 +0800 Subject: [PATCH 246/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bf02a5d8e2..c61977cfa3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 4f973dbeb9..6389172fe7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 362a7b2c7735fa753e81c7075dba38b4d634e1be Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 19 Apr 2024 18:03:13 +0900 Subject: [PATCH 247/581] Remove unused members from GameplaySkinComponentLookup --- osu.Game.Rulesets.Catch/CatchSkinComponentLookup.cs | 4 ---- osu.Game.Rulesets.Mania/ManiaSkinComponentLookup.cs | 4 ---- osu.Game.Rulesets.Osu/OsuSkinComponentLookup.cs | 4 ---- osu.Game.Rulesets.Taiko/TaikoSkinComponentLookup.cs | 4 ---- osu.Game/Skinning/GameplaySkinComponentLookup.cs | 3 --- 5 files changed, 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponentLookup.cs b/osu.Game.Rulesets.Catch/CatchSkinComponentLookup.cs index 149aae1cb4..596b102ac5 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponentLookup.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponentLookup.cs @@ -11,9 +11,5 @@ namespace osu.Game.Rulesets.Catch : base(component) { } - - protected override string RulesetPrefix => "catch"; // todo: use CatchRuleset.SHORT_NAME; - - protected override string ComponentName => Component.ToString().ToLowerInvariant(); } } diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponentLookup.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponentLookup.cs index 44120e16e6..046d1c5b34 100644 --- a/osu.Game.Rulesets.Mania/ManiaSkinComponentLookup.cs +++ b/osu.Game.Rulesets.Mania/ManiaSkinComponentLookup.cs @@ -15,10 +15,6 @@ namespace osu.Game.Rulesets.Mania : base(component) { } - - protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME; - - protected override string ComponentName => Component.ToString().ToLowerInvariant(); } public enum ManiaSkinComponents diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponentLookup.cs b/osu.Game.Rulesets.Osu/OsuSkinComponentLookup.cs index 81d5811f85..3b3653e1ba 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponentLookup.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponentLookup.cs @@ -11,9 +11,5 @@ namespace osu.Game.Rulesets.Osu : base(component) { } - - protected override string RulesetPrefix => OsuRuleset.SHORT_NAME; - - protected override string ComponentName => Component.ToString().ToLowerInvariant(); } } diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponentLookup.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponentLookup.cs index c35971e9fd..8841c3d3ca 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponentLookup.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponentLookup.cs @@ -11,9 +11,5 @@ namespace osu.Game.Rulesets.Taiko : base(component) { } - - protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME; - - protected override string ComponentName => Component.ToString().ToLowerInvariant(); } } diff --git a/osu.Game/Skinning/GameplaySkinComponentLookup.cs b/osu.Game/Skinning/GameplaySkinComponentLookup.cs index ec159873f8..c317a17e21 100644 --- a/osu.Game/Skinning/GameplaySkinComponentLookup.cs +++ b/osu.Game/Skinning/GameplaySkinComponentLookup.cs @@ -24,8 +24,5 @@ namespace osu.Game.Skinning { Component = component; } - - protected virtual string RulesetPrefix => string.Empty; - protected virtual string ComponentName => Component.ToString(); } } From 9d04b44a8872663476798264d8ab91943768f0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Apr 2024 11:04:05 +0200 Subject: [PATCH 248/581] Add failing test case --- .../UserInterface/TestSceneModSelectOverlay.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 6c75530a6e..8ddbd84890 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -612,6 +612,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus); } + [Test] + public void TestSearchBoxFocusToggleRespondsToExternalChanges() + { + AddStep("text search does not start active", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, false)); + createScreen(); + + AddUntilStep("search text box not focused", () => !modSelectOverlay.SearchTextBox.HasFocus); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); + + AddStep("unfocus search text box externally", () => InputManager.ChangeFocus(null)); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); + } + [Test] public void TestTextSearchDoesNotBlockCustomisationPanelKeyboardInteractions() { From 2dcbb823ef327f947547ed8f74990e74d021712d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Apr 2024 11:08:34 +0200 Subject: [PATCH 249/581] Use textbox focus state directly rather than trying to track it independently --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 5ca26c739e..47362c0003 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -143,8 +143,6 @@ namespace osu.Game.Overlays.Mods protected ShearedToggleButton? CustomisationButton { get; private set; } protected SelectAllModsButton? SelectAllModsButton { get; set; } - private bool textBoxShouldFocus; - private Sample? columnAppearSample; private WorkingBeatmap? beatmap; @@ -542,7 +540,7 @@ namespace osu.Game.Overlays.Mods if (customisationVisible.Value) SearchTextBox.KillFocus(); else - setTextBoxFocus(textBoxShouldFocus); + setTextBoxFocus(textSearchStartsActive.Value); } /// @@ -798,15 +796,13 @@ namespace osu.Game.Overlays.Mods return false; // TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`) - setTextBoxFocus(!textBoxShouldFocus); + setTextBoxFocus(!SearchTextBox.HasFocus); return true; } - private void setTextBoxFocus(bool keepFocus) + private void setTextBoxFocus(bool focus) { - textBoxShouldFocus = keepFocus; - - if (textBoxShouldFocus) + if (focus) SearchTextBox.TakeFocus(); else SearchTextBox.KillFocus(); From 509862490e88f5ebea63005e23463336cb43f9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 19 Apr 2024 11:11:18 +0200 Subject: [PATCH 250/581] Revert unnecessary changes --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 47362c0003..25293e8e20 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -418,7 +418,7 @@ namespace osu.Game.Overlays.Mods yield return new ColumnDimContainer(new ModPresetColumn { Margin = new MarginPadding { Right = 10 } - }, this); + }); } yield return createModColumnContent(ModType.DifficultyReduction); @@ -436,7 +436,7 @@ namespace osu.Game.Overlays.Mods column.Margin = new MarginPadding { Right = 10 }; }); - return new ColumnDimContainer(column, this); + return new ColumnDimContainer(column); } private void createLocalMods() @@ -895,17 +895,13 @@ namespace osu.Game.Overlays.Mods [Resolved] private OsuColour colours { get; set; } = null!; - private ModSelectOverlay modSelectOverlayInstance; - - public ColumnDimContainer(ModSelectColumn column, ModSelectOverlay modSelectOverlay) + public ColumnDimContainer(ModSelectColumn column) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; Child = Column = column; column.Active.BindTo(Active); - - this.modSelectOverlayInstance = modSelectOverlay; } [BackgroundDependencyLoader] @@ -953,7 +949,7 @@ namespace osu.Game.Overlays.Mods RequestScroll?.Invoke(this); // Killing focus is done here because it's the only feasible place on ModSelectOverlay you can click on without triggering any action. - modSelectOverlayInstance.setTextBoxFocus(false); + Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null)); return true; } From eac9dededfb59ab668cedc0e788aef11532cf152 Mon Sep 17 00:00:00 2001 From: Arthur Araujo <90941580+64ArthurAraujo@users.noreply.github.com> Date: Fri, 19 Apr 2024 07:20:59 -0300 Subject: [PATCH 251/581] Use periods instead of semicolons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index 3343d07f25..d716f5ba3c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateFormatUnsupported : IssueTemplate { public IssueTemplateFormatUnsupported(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format; Use wav or ogg for hitsounds.") + : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format. Use wav or ogg for hitsounds.") { } @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateIncorrectFormat : IssueTemplate { public IssueTemplateIncorrectFormat(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}); Use wav or ogg for hitsounds.") + : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}). Use wav or ogg for hitsounds.") { } From 68e86b5105fae6a4dd72bada15774b4ad2a052e0 Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Fri, 19 Apr 2024 07:38:55 -0300 Subject: [PATCH 252/581] Remove unsued import and add a blankline before `yield break;` --- osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs index 9ed1350f56..7dd469833e 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -48,6 +47,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (decodeStream == 0) { yield return new IssueTemplateFormatUnsupported(this).Create(audioFile.Filename); + yield break; } From 92b85beff6735e83f3324427478833d0b62d504d Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Fri, 19 Apr 2024 08:01:23 -0300 Subject: [PATCH 253/581] Remove `ChannelType` from messages --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 6 +++--- osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index d716f5ba3c..184311b1c3 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Edit.Checks var audioInfo = Bass.ChannelGetInfo(decodeStream); if (!allowedFormats.Contains(audioInfo.ChannelType)) - yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename, audioInfo.ChannelType.ToString()); + yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename); Bass.StreamFree(decodeStream); } @@ -83,11 +83,11 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateIncorrectFormat : IssueTemplate { public IssueTemplateIncorrectFormat(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}). Use wav or ogg for hitsounds.") + : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format. Use wav or ogg for hitsounds.") { } - public Issue Create(string file, string format) => new Issue(this, file, format); + public Issue Create(string file) => new Issue(this, file); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs index 7dd469833e..0cb54709af 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Edit.Checks var audioInfo = Bass.ChannelGetInfo(decodeStream); if (!allowedFormats.Contains(audioInfo.ChannelType)) - yield return new IssueTemplateIncorrectFormat(this).Create(audioFile.Filename, audioInfo.ChannelType.ToString()); + yield return new IssueTemplateIncorrectFormat(this).Create(audioFile.Filename); Bass.StreamFree(decodeStream); } @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateFormatUnsupported : IssueTemplate { public IssueTemplateFormatUnsupported(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format; Use mp3 or ogg for the song's audio.") + : base(check, IssueType.Problem, "\"{0}\" may be corrupt or using a unsupported audio format. Use mp3 or ogg for the song's audio.") { } @@ -73,11 +73,11 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateIncorrectFormat : IssueTemplate { public IssueTemplateIncorrectFormat(ICheck check) - : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format ({1}); Use mp3 or ogg for the song's audio.") + : base(check, IssueType.Problem, "\"{0}\" is using a incorrect format. Use mp3 or ogg for the song's audio.") { } - public Issue Create(string file, string format) => new Issue(this, file, format); + public Issue Create(string file) => new Issue(this, file); } } } From ddc1b90ee1836662e788e02f266992cecad91e91 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 19 Apr 2024 20:36:24 +0900 Subject: [PATCH 254/581] Remove unused method --- .../Skinning/ISerialisableDrawableContainer.cs | 5 ----- osu.Game/Skinning/SkinComponentsContainer.cs | 15 --------------- 2 files changed, 20 deletions(-) diff --git a/osu.Game/Skinning/ISerialisableDrawableContainer.cs b/osu.Game/Skinning/ISerialisableDrawableContainer.cs index a19c8c5162..57ea75bc7e 100644 --- a/osu.Game/Skinning/ISerialisableDrawableContainer.cs +++ b/osu.Game/Skinning/ISerialisableDrawableContainer.cs @@ -30,11 +30,6 @@ namespace osu.Game.Skinning /// void Reload(); - /// - /// Reload this target from the provided skinnable information. - /// - void Reload(SerialisedDrawableInfo[] skinnableInfo); - /// /// Add a new skinnable component to this target. /// diff --git a/osu.Game/Skinning/SkinComponentsContainer.cs b/osu.Game/Skinning/SkinComponentsContainer.cs index adf0a288b4..02ba43fd39 100644 --- a/osu.Game/Skinning/SkinComponentsContainer.cs +++ b/osu.Game/Skinning/SkinComponentsContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using osu.Framework.Bindables; @@ -44,20 +43,6 @@ namespace osu.Game.Skinning Lookup = lookup; } - public void Reload(SerialisedDrawableInfo[] skinnableInfo) - { - var drawables = new List(); - - foreach (var i in skinnableInfo) - drawables.Add(i.CreateInstance()); - - Reload(new Container - { - RelativeSizeAxes = Axes.Both, - Children = drawables, - }); - } - public void Reload() => Reload(CurrentSkin.GetDrawableComponent(Lookup) as Container); public void Reload(Container? componentsContainer) From bac70da1a1a12ddae4769da8ead2374ed6932311 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 19 Apr 2024 21:40:02 +0900 Subject: [PATCH 255/581] Give SerialisedDrawableInfo sane defaults --- osu.Game/Skinning/SerialisedDrawableInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/SerialisedDrawableInfo.cs b/osu.Game/Skinning/SerialisedDrawableInfo.cs index 2d6113ff70..ac1aa80d29 100644 --- a/osu.Game/Skinning/SerialisedDrawableInfo.cs +++ b/osu.Game/Skinning/SerialisedDrawableInfo.cs @@ -34,15 +34,15 @@ namespace osu.Game.Skinning public float Rotation { get; set; } - public Vector2 Scale { get; set; } + public Vector2 Scale { get; set; } = Vector2.One; public float? Width { get; set; } public float? Height { get; set; } - public Anchor Anchor { get; set; } + public Anchor Anchor { get; set; } = Anchor.TopLeft; - public Anchor Origin { get; set; } + public Anchor Origin { get; set; } = Anchor.TopLeft; /// public bool UsesFixedAnchor { get; set; } From 4cffc39dffde5e14172df2693e71eebb4f3964ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 20 Apr 2024 04:53:31 +0800 Subject: [PATCH 256/581] Don't require hold for quick exit at results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index f28b9b2554..ebb0530046 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -191,16 +191,6 @@ namespace osu.Game.Screens.Ranking }); } - AddInternal(new HotkeyExitOverlay - { - Action = () => - { - if (!this.IsCurrentScreen()) return; - - this.Exit(); - }, - }); - if (player != null && AllowRetry) { buttons.Add(new RetryButton { Width = 300 }); @@ -400,6 +390,15 @@ namespace osu.Game.Screens.Ranking switch (e.Action) { + case GlobalAction.QuickExit: + if (this.IsCurrentScreen()) + { + this.Exit(); + return true; + } + + break; + case GlobalAction.Select: StatisticsPanel.ToggleVisibility(); return true; From 722fa228cedfb81726dcc08e7b2baa4c524fb958 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Apr 2024 17:40:35 +0300 Subject: [PATCH 257/581] Don't consider editor table content for input --- osu.Game/Screens/Edit/EditorTable.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index e5dc540b06..49b41fac21 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -30,6 +30,10 @@ namespace osu.Game.Screens.Edit protected readonly FillFlowContainer BackgroundFlow; + // We can avoid potentially thousands of objects being added to the input sub-tree since item selection is being handled by the BackgroundFlow + // and no items in the underlying table are clickable. + protected override bool ShouldBeConsideredForInput(Drawable child) => base.ShouldBeConsideredForInput(child) && child == BackgroundFlow; + protected EditorTable() { RelativeSizeAxes = Axes.X; From 1d3fd65d86f517805f573517ad7650d1a89277af Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Apr 2024 07:02:49 +0300 Subject: [PATCH 258/581] Adjust execution order --- osu.Game/Screens/Edit/EditorTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 49b41fac21..4d8393e829 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit // We can avoid potentially thousands of objects being added to the input sub-tree since item selection is being handled by the BackgroundFlow // and no items in the underlying table are clickable. - protected override bool ShouldBeConsideredForInput(Drawable child) => base.ShouldBeConsideredForInput(child) && child == BackgroundFlow; + protected override bool ShouldBeConsideredForInput(Drawable child) => child == BackgroundFlow && base.ShouldBeConsideredForInput(child); protected EditorTable() { From 40c48f903bdfdb0f11809c5aeea268aec198fbb6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 22 Apr 2024 14:58:45 +0900 Subject: [PATCH 259/581] Add failing test --- .../TestSceneCursorTrail.cs | 31 +++++++++++++++---- .../Skinning/Legacy/LegacyCursorTrail.cs | 16 +++++----- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index 421a32b9eb..afb0246b21 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; @@ -13,6 +14,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; using osu.Framework.Testing.Input; using osu.Game.Audio; using osu.Game.Rulesets.Osu.Skinning.Legacy; @@ -70,6 +72,22 @@ namespace osu.Game.Rulesets.Osu.Tests }); } + [Test] + public void TestLegacyDisjointCursorTrailViaNoCursor() + { + createTest(() => + { + var skinContainer = new LegacySkinContainer(renderer, false, false); + var legacyCursorTrail = new LegacyCursorTrail(skinContainer); + + skinContainer.Child = legacyCursorTrail; + + return skinContainer; + }); + + AddAssert("trail is disjoint", () => this.ChildrenOfType().Single().DisjointTrail, () => Is.True); + } + private void createTest(Func createContent) => AddStep("create trail", () => { Clear(); @@ -87,11 +105,13 @@ namespace osu.Game.Rulesets.Osu.Tests { private readonly IRenderer renderer; private readonly bool disjoint; + private readonly bool provideCursor; - public LegacySkinContainer(IRenderer renderer, bool disjoint) + public LegacySkinContainer(IRenderer renderer, bool disjoint, bool provideCursor = true) { this.renderer = renderer; this.disjoint = disjoint; + this.provideCursor = provideCursor; RelativeSizeAxes = Axes.Both; } @@ -102,12 +122,11 @@ namespace osu.Game.Rulesets.Osu.Tests { switch (componentName) { - case "cursortrail": - var tex = new Texture(renderer.WhitePixel); + case "cursor": + return provideCursor ? new Texture(renderer.WhitePixel) : null; - if (disjoint) - tex.ScaleAdjust = 1 / 25f; - return tex; + case "cursortrail": + return new Texture(renderer.WhitePixel); case "cursormiddle": return disjoint ? null : renderer.WhitePixel; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index af71e2a5d9..4ebafd0c4b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private readonly ISkin skin; private const double disjoint_trail_time_separation = 1000 / 60.0; - private bool disjointTrail; + public bool DisjointTrail { get; private set; } private double lastTrailTime; private IBindable cursorSize = null!; @@ -36,9 +36,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy cursorSize = config.GetBindable(OsuSetting.GameplayCursorSize).GetBoundCopy(); Texture = skin.GetTexture("cursortrail"); - disjointTrail = skin.GetTexture("cursormiddle") == null; + DisjointTrail = skin.GetTexture("cursormiddle") == null; - if (disjointTrail) + if (DisjointTrail) { bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? true; @@ -57,19 +57,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } } - protected override double FadeDuration => disjointTrail ? 150 : 500; + protected override double FadeDuration => DisjointTrail ? 150 : 500; protected override float FadeExponent => 1; - protected override bool InterpolateMovements => !disjointTrail; + protected override bool InterpolateMovements => !DisjointTrail; protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1); - protected override bool AvoidDrawingNearCursor => !disjointTrail; + protected override bool AvoidDrawingNearCursor => !DisjointTrail; protected override void Update() { base.Update(); - if (!disjointTrail || !currentPosition.HasValue) + if (!DisjointTrail || !currentPosition.HasValue) return; if (Time.Current - lastTrailTime >= disjoint_trail_time_separation) @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override bool OnMouseMove(MouseMoveEvent e) { - if (!disjointTrail) + if (!DisjointTrail) return base.OnMouseMove(e); currentPosition = e.ScreenSpaceMousePosition; From 0170c04baf447abd9a4f986bcc169f940e92f9d4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 22 Apr 2024 15:00:09 +0900 Subject: [PATCH 260/581] Fix `cursormiddle` not using the same source as `cursor` --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index 4ebafd0c4b..da52956719 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -31,12 +31,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, ISkinSource skinSource) { cursorSize = config.GetBindable(OsuSetting.GameplayCursorSize).GetBoundCopy(); Texture = skin.GetTexture("cursortrail"); - DisjointTrail = skin.GetTexture("cursormiddle") == null; + + var cursorProvider = skinSource.FindProvider(s => s.GetTexture("cursor") != null); + DisjointTrail = cursorProvider?.GetTexture("cursormiddle") == null; if (DisjointTrail) { From fb1d20bd39605eca96837d2d8e9b2cc394800cba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2024 15:07:11 +0800 Subject: [PATCH 261/581] Comment in some clarification --- .../TestSceneCursorTrail.cs | 14 +++++++------- .../Skinning/Legacy/LegacyCursorTrail.cs | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index afb0246b21..4db66fde4b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Tests { createTest(() => { - var skinContainer = new LegacySkinContainer(renderer, false); + var skinContainer = new LegacySkinContainer(renderer, provideMiddle: false); var legacyCursorTrail = new LegacyCursorTrail(skinContainer); skinContainer.Child = legacyCursorTrail; @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Tests { createTest(() => { - var skinContainer = new LegacySkinContainer(renderer, true); + var skinContainer = new LegacySkinContainer(renderer, provideMiddle: true); var legacyCursorTrail = new LegacyCursorTrail(skinContainer); skinContainer.Child = legacyCursorTrail; @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Tests { createTest(() => { - var skinContainer = new LegacySkinContainer(renderer, false, false); + var skinContainer = new LegacySkinContainer(renderer, provideMiddle: false, provideCursor: false); var legacyCursorTrail = new LegacyCursorTrail(skinContainer); skinContainer.Child = legacyCursorTrail; @@ -104,13 +104,13 @@ namespace osu.Game.Rulesets.Osu.Tests private partial class LegacySkinContainer : Container, ISkinSource { private readonly IRenderer renderer; - private readonly bool disjoint; + private readonly bool provideMiddle; private readonly bool provideCursor; - public LegacySkinContainer(IRenderer renderer, bool disjoint, bool provideCursor = true) + public LegacySkinContainer(IRenderer renderer, bool provideMiddle, bool provideCursor = true) { this.renderer = renderer; - this.disjoint = disjoint; + this.provideMiddle = provideMiddle; this.provideCursor = provideCursor; RelativeSizeAxes = Axes.Both; @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Osu.Tests return new Texture(renderer.WhitePixel); case "cursormiddle": - return disjoint ? null : renderer.WhitePixel; + return provideMiddle ? null : renderer.WhitePixel; } return null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index da52956719..ca0002d8c0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -37,6 +37,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Texture = skin.GetTexture("cursortrail"); + // Cursor and cursor trail components are sourced from potentially different skin sources. + // Stable always chooses cursor trail disjoint behaviour based on the cursor texture lookup source, so we need to fetch where that occurred. + // See https://github.com/peppy/osu-stable-reference/blob/3ea48705eb67172c430371dcfc8a16a002ed0d3d/osu!/Graphics/Skinning/SkinManager.cs#L269 var cursorProvider = skinSource.FindProvider(s => s.GetTexture("cursor") != null); DisjointTrail = cursorProvider?.GetTexture("cursormiddle") == null; From 49563f4e5bc9ce3ea72b5645456a6d0e8af6a89c Mon Sep 17 00:00:00 2001 From: Arthur Araujo Date: Mon, 22 Apr 2024 04:44:38 -0300 Subject: [PATCH 262/581] Use bitwise to check hitsound formats --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index 184311b1c3..9779696e4b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using ManagedBass; using osu.Framework.Audio.Callbacks; using osu.Game.Beatmaps; @@ -22,14 +21,6 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateIncorrectFormat(this), }; - private IEnumerable allowedFormats => new ChannelType[] - { - ChannelType.WavePCM, - ChannelType.WaveFloat, - ChannelType.OGG, - ChannelType.Wave | ChannelType.OGG, - }; - public IEnumerable Run(BeatmapVerifierContext context) { var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet; @@ -60,7 +51,7 @@ namespace osu.Game.Rulesets.Edit.Checks var audioInfo = Bass.ChannelGetInfo(decodeStream); - if (!allowedFormats.Contains(audioInfo.ChannelType)) + if ((audioInfo.ChannelType & ChannelType.Wave) == 0 && audioInfo.ChannelType != ChannelType.OGG) yield return new IssueTemplateIncorrectFormat(this).Create(file.Filename); Bass.StreamFree(decodeStream); From 09b0f3005e28cb2248ddb206b80d3eb3a94c6e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Apr 2024 10:15:56 +0200 Subject: [PATCH 263/581] Apply generic math-related changes --- osu.Game/Graphics/UserInterface/ExpandableSlider.cs | 8 ++++---- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 9 +++++---- osu.Game/Graphics/UserInterface/RoundedSliderBar.cs | 5 +++-- osu.Game/Graphics/UserInterface/ShearedSliderBar.cs | 5 +++-- osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs | 4 ++-- .../Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs | 8 ++++---- osu.Game/Overlays/Settings/Sections/SizeSlider.cs | 3 ++- osu.Game/Overlays/Settings/SettingsPercentageSlider.cs | 4 ++-- osu.Game/Overlays/Settings/SettingsSlider.cs | 6 +++--- .../Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 8 ++++---- osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs | 4 ++-- 11 files changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ExpandableSlider.cs b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs index 121a1eef49..a7a8561b94 100644 --- a/osu.Game/Graphics/UserInterface/ExpandableSlider.cs +++ b/osu.Game/Graphics/UserInterface/ExpandableSlider.cs @@ -1,7 +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 System; +using System.Numerics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -10,7 +10,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osuTK; +using Vector2 = osuTK.Vector2; namespace osu.Game.Graphics.UserInterface { @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface /// An implementation for the UI slider bar control. /// public partial class ExpandableSlider : CompositeDrawable, IExpandable, IHasCurrentValue - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue where TSlider : RoundedSliderBar, new() { private readonly OsuSpriteText label; @@ -129,7 +129,7 @@ namespace osu.Game.Graphics.UserInterface /// An implementation for the UI slider bar control. /// public partial class ExpandableSlider : ExpandableSlider> - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { } } diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 191a7ca246..9cb6356cab 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Numerics; using System.Globalization; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -15,7 +16,7 @@ using osu.Game.Utils; namespace osu.Game.Graphics.UserInterface { public abstract partial class OsuSliderBar : SliderBar, IHasTooltip - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { public bool PlaySamplesOnAdjust { get; set; } = true; @@ -85,11 +86,11 @@ namespace osu.Game.Graphics.UserInterface private LocalisableString getTooltipText(T value) { if (CurrentNumber.IsInteger) - return value.ToInt32(NumberFormatInfo.InvariantInfo).ToString("N0"); + return int.CreateTruncating(value).ToString("N0"); - double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo); + double floatValue = double.CreateTruncating(value); - decimal decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); + decimal decimalPrecision = normalise(decimal.CreateTruncating(CurrentNumber.Precision), max_decimal_digits); // Find the number of significant digits (we could have less than 5 after normalize()) int significantDigits = FormatUtils.FindPrecision(decimalPrecision); diff --git a/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs b/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs index 0981881ead..56047173bb 100644 --- a/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/RoundedSliderBar.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; +using System.Numerics; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -11,11 +11,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Overlays; +using Vector2 = osuTK.Vector2; namespace osu.Game.Graphics.UserInterface { public partial class RoundedSliderBar : OsuSliderBar - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { protected readonly Nub Nub; protected readonly Box LeftBox; diff --git a/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs b/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs index 60a6670492..0df1c1d204 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; +using System.Numerics; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -12,11 +12,12 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Overlays; using static osu.Game.Graphics.UserInterface.ShearedNub; +using Vector2 = osuTK.Vector2; namespace osu.Game.Graphics.UserInterface { public partial class ShearedSliderBar : OsuSliderBar - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { protected readonly ShearedNub Nub; protected readonly Box LeftBox; diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs index 4585d3a4c9..4912a21fab 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs @@ -1,14 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Numerics; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; namespace osu.Game.Graphics.UserInterfaceV2 { public partial class LabelledSliderBar : LabelledComponent, TNumber> - where TNumber : struct, IEquatable, IComparable, IConvertible + where TNumber : struct, INumber, IMinMaxValue { public LabelledSliderBar() : base(true) diff --git a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index e5ba7f61bf..abd828e98f 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -1,7 +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 System; +using System.Numerics; using System.Globalization; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -10,12 +10,12 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Utils; -using osuTK; +using Vector2 = osuTK.Vector2; namespace osu.Game.Graphics.UserInterfaceV2 { public partial class SliderWithTextBoxInput : CompositeDrawable, IHasCurrentValue - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { /// /// A custom step value for each key press which actuates a change on this control. @@ -138,7 +138,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { if (updatingFromTextBox) return; - decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo); + decimal decimalValue = decimal.CreateTruncating(slider.Current.Value); textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}"); } } diff --git a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs index c73831d8d1..14ef58ff88 100644 --- a/osu.Game/Overlays/Settings/Sections/SizeSlider.cs +++ b/osu.Game/Overlays/Settings/Sections/SizeSlider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Numerics; using System.Globalization; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; @@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections /// A slider intended to show a "size" multiplier number, where 1x is 1.0. /// public partial class SizeSlider : RoundedSliderBar - where T : struct, IEquatable, IComparable, IConvertible, IFormattable + where T : struct, INumber, IMinMaxValue, IFormattable { public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo); } diff --git a/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs b/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs index fa59d18de1..d7a09d3392 100644 --- a/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs +++ b/osu.Game/Overlays/Settings/SettingsPercentageSlider.cs @@ -1,7 +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 System; +using System.Numerics; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings /// Mostly provided for convenience of use with . /// public partial class SettingsPercentageSlider : SettingsSlider - where TValue : struct, IEquatable, IComparable, IConvertible + where TValue : struct, INumber, IMinMaxValue { protected override Drawable CreateControl() => ((RoundedSliderBar)base.CreateControl()).With(sliderBar => sliderBar.DisplayAsPercentage = true); } diff --git a/osu.Game/Overlays/Settings/SettingsSlider.cs b/osu.Game/Overlays/Settings/SettingsSlider.cs index 6c81fece13..2460d78099 100644 --- a/osu.Game/Overlays/Settings/SettingsSlider.cs +++ b/osu.Game/Overlays/Settings/SettingsSlider.cs @@ -1,7 +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 System; +using System.Numerics; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; @@ -9,12 +9,12 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { public partial class SettingsSlider : SettingsSlider> - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { } public partial class SettingsSlider : SettingsItem - where TValue : struct, IEquatable, IComparable, IConvertible + where TValue : struct, INumber, IMinMaxValue where TSlider : RoundedSliderBar, new() { protected override Drawable CreateControl() => new TSlider diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index 151d469415..26f374ba85 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -1,7 +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 System; +using System.Numerics; using System.Globalization; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -12,7 +12,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; using osu.Game.Utils; -using osuTK; +using Vector2 = osuTK.Vector2; namespace osu.Game.Screens.Edit.Timing { @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Timing /// by providing an "indeterminate state". /// public partial class IndeterminateSliderWithTextBoxInput : CompositeDrawable, IHasCurrentValue - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { /// /// A custom step value for each key press which actuates a change on this control. @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Edit.Timing slider.Current.Value = nonNullValue; // use the value from the slider to ensure that any precision/min/max set on it via the initial indeterminate value have been applied correctly. - decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo); + decimal decimalValue = decimal.CreateTruncating(slider.Current.Value); textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}"); textBox.PlaceholderText = string.Empty; } diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs index 88b778fafb..1fc1155c0b 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs @@ -1,7 +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 System; +using System.Numerics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; @@ -11,7 +11,7 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Screens.Play.PlayerSettings { public partial class PlayerSliderBar : SettingsSlider - where T : struct, IEquatable, IComparable, IConvertible + where T : struct, INumber, IMinMaxValue { public RoundedSliderBar Bar => (RoundedSliderBar)Control; From 1bcf835d227ce3899d1e16ff16cfba4a9c08dcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Apr 2024 10:25:46 +0200 Subject: [PATCH 264/581] Remove redundant array type specification --- osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs index 0cb54709af..dd01fe110a 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckSongFormat.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateIncorrectFormat(this), }; - private IEnumerable allowedFormats => new ChannelType[] + private IEnumerable allowedFormats => new[] { ChannelType.MP3, ChannelType.OGG, From ceeb9b081903dab467a5659595f6746403ccc845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Apr 2024 10:27:30 +0200 Subject: [PATCH 265/581] Use explicit reference equality check --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index 9779696e4b..728567b490 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Edit.Checks foreach (var file in beatmapSet.Files) { - if (audioFile != null && file.File == audioFile.File) continue; + if (audioFile != null && ReferenceEquals(file.File, audioFile.File)) continue; using (Stream data = context.WorkingBeatmap.GetStream(file.File.GetStoragePath())) { From b28bf4d2ecf2dbc6b11b01ffdca5ae6adbcaecaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Apr 2024 10:43:20 +0200 Subject: [PATCH 266/581] Add test covering non-audio file formats not being checked --- .../Checks/CheckHitsoundsFormatTest.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs index 9a806f6cb7..cb1cf21734 100644 --- a/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckHitsoundsFormatTest.cs @@ -85,6 +85,27 @@ namespace osu.Game.Tests.Editing.Checks } } + [Test] + public void TestNotAnAudioFile() + { + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo + { + Files = { CheckTestHelpers.CreateMockFile("png") } + } + } + }; + + using (var resourceStream = TestResources.OpenResource("Textures/test-image.png")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + Assert.That(issues, Has.Count.EqualTo(0)); + } + } + [Test] public void TestCorruptAudio() { From 70a5288a6859dae28f7870f338e82cbbdcf4c1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Apr 2024 10:45:09 +0200 Subject: [PATCH 267/581] Do not attempt to pass files that don't look like audio to BASS --- osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs index 728567b490..9b6a861358 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckHitsoundsFormat.cs @@ -37,14 +37,16 @@ namespace osu.Game.Rulesets.Edit.Checks if (data == null) continue; + if (!AudioCheckUtils.HasAudioExtension(file.Filename) || !probablyHasAudioData(data)) + continue; + var fileCallbacks = new FileCallbacks(new DataStreamFileProcedures(data)); int decodeStream = Bass.CreateStream(StreamSystem.NoBuffer, BassFlags.Decode, fileCallbacks.Callbacks, fileCallbacks.Handle); // If the format is not supported by BASS if (decodeStream == 0) { - if (AudioCheckUtils.HasAudioExtension(file.Filename) && probablyHasAudioData(data)) - yield return new IssueTemplateFormatUnsupported(this).Create(file.Filename); + yield return new IssueTemplateFormatUnsupported(this).Create(file.Filename); continue; } From 4ae9f81c73f39828e4e812cc362bacbda2656c9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2024 18:43:15 +0800 Subject: [PATCH 268/581] Apply transforms to storyboard videos This requires that they are a `StoryboardSprite` for simplicity. Luckily this works just fine. --- .../Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Storyboards/StoryboardVideo.cs | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 6689f087cb..b5d9ad1194 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps.Formats if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant())) break; - storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset)); + storyboard.GetLayer("Video").Add(storyboardSprite = new StoryboardVideo(path, offset)); break; } diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 8c11e19a06..bd1b933b6f 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -3,23 +3,18 @@ using osu.Framework.Graphics; using osu.Game.Storyboards.Drawables; +using osuTK; namespace osu.Game.Storyboards { - public class StoryboardVideo : IStoryboardElement + public class StoryboardVideo : StoryboardSprite { - public string Path { get; } - - public bool IsDrawable => true; - - public double StartTime { get; } - public StoryboardVideo(string path, double offset) + : base(path, Anchor.Centre, Vector2.Zero) { - Path = path; - StartTime = offset; + TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 1); } - public Drawable CreateDrawable() => new DrawableStoryboardVideo(this); + public override Drawable CreateDrawable() => new DrawableStoryboardVideo(this); } } From c43c383abf5d05caa5827b23b1fd53f61cff5b55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2024 18:43:34 +0800 Subject: [PATCH 269/581] Allow storboard videos to take on no-relative size when specified by a mapper --- .../Drawables/DrawableStoryboardVideo.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 9a5db4bb39..98cb01d5f3 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -23,7 +23,17 @@ namespace osu.Game.Storyboards.Drawables { Video = video; - RelativeSizeAxes = Axes.Both; + // In osu-stable, a mapper can add a scale command for a storyboard. + // This allows scaling based on the video's absolute size. + // + // If not specified we take up the full available space. + bool useRelative = !video.TimelineGroup.Scale.HasCommands; + + RelativeSizeAxes = useRelative ? Axes.Both : Axes.None; + AutoSizeAxes = useRelative ? Axes.None : Axes.Both; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; } [BackgroundDependencyLoader(true)] @@ -36,7 +46,7 @@ namespace osu.Game.Storyboards.Drawables InternalChild = drawableVideo = new Video(stream, false) { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = RelativeSizeAxes, FillMode = FillMode.Fill, Anchor = Anchor.Centre, Origin = Anchor.Centre, From 9e7182acf0cfa3fc658758b78af4dc7378cef536 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2024 18:45:02 +0800 Subject: [PATCH 270/581] Remove unused DI beatmap paramater --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 98cb01d5f3..ca2c7b774c 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; -using osu.Game.Beatmaps; namespace osu.Game.Storyboards.Drawables { @@ -37,7 +35,7 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader(true)] - private void load(IBindable beatmap, TextureStore textureStore) + private void load(TextureStore textureStore) { var stream = textureStore.GetStream(Video.Path); From 2eda56ff0859a0e0fe44e59d2b7f936139dcc94b Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 22 Apr 2024 11:15:50 -0700 Subject: [PATCH 271/581] Revert beatmap query change --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ccdf9d151f..98533a5c82 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -716,7 +716,7 @@ namespace osu.Game return; } - var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.OnlineID == score.Beatmap.OnlineID); + var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScore.ScoreInfo.BeatmapInfo.ID); if (databasedBeatmap == null) { From a59bf6d373cfdc234261fc1ce2d489803bd35f14 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 22 Apr 2024 11:17:27 -0700 Subject: [PATCH 272/581] Improve xmldoc wording --- osu.Game/Scoring/ScoreManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index b6bb637537..f37ee2b101 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -62,7 +62,7 @@ namespace osu.Game.Scoring /// Retrieve a from a given . /// /// The to convert. - /// The . Null if the score on the database cannot be found. + /// The . Null if the score cannot be found in the database. /// /// The is re-retrieved from the database to ensure all the required data /// for retrieving a replay are present (may have missing properties if it was retrieved from online data). @@ -201,7 +201,7 @@ namespace osu.Game.Scoring /// Export a replay from a given . /// /// The to export. - /// The . Null if the score on the database cannot be found. + /// The . Null if the score cannot be found in the database. /// /// The is re-retrieved from the database to ensure all the required data /// for exporting a replay are present (may have missing properties if it was retrieved from online data). From 35eddf35c5651b883a65e955a750ddd2c05b47be Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 22 Apr 2024 11:22:39 -0700 Subject: [PATCH 273/581] Return `Task.CompletedTask` instead of `null` --- osu.Game/Scoring/ScoreManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index f37ee2b101..1ba5c7d4cf 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -201,16 +201,16 @@ namespace osu.Game.Scoring /// Export a replay from a given . /// /// The to export. - /// The . Null if the score cannot be found in the database. + /// The . Return if the score cannot be found in the database. /// /// The is re-retrieved from the database to ensure all the required data /// for exporting a replay are present (may have missing properties if it was retrieved from online data). /// - public Task? Export(ScoreInfo scoreInfo) + public Task Export(ScoreInfo scoreInfo) { ScoreInfo? databasedScoreInfo = getDatabasedScoreInfo(scoreInfo); - return databasedScoreInfo == null ? null : scoreExporter.ExportAsync(databasedScoreInfo.ToLive(Realm)); + return databasedScoreInfo == null ? Task.CompletedTask : scoreExporter.ExportAsync(databasedScoreInfo.ToLive(Realm)); } public Task?> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); From 49154c0e231c25f78c849ecf7180b4240b9e0145 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 22 Apr 2024 11:23:38 -0700 Subject: [PATCH 274/581] Fix code quality --- osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs | 2 +- .../Visual/UserInterface/TestSceneDeleteLocalScore.cs | 2 +- osu.Game/Screens/Play/Player.cs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 6590339311..004d1de116 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -170,7 +170,7 @@ namespace osu.Game.Tests.Visual.Navigation BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo, User = new GuestUser(), - }).Value; + })!.Value; }); AddAssert($"import {i} succeeded", () => imported != null); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 529874b71e..e2fe10fa74 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.UserInterface Files = { new RealmNamedFileUsage(new RealmFile { Hash = $"{i}" }, string.Empty) } }; - importedScores.Add(scoreManager.Import(score).Value); + importedScores.Add(scoreManager.Import(score)!.Value); } }); }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 4fcc52bc5d..3a80caf259 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1200,6 +1200,7 @@ namespace osu.Game.Screens.Play var importableScore = score.ScoreInfo.DeepClone(); var imported = scoreManager.Import(importableScore, replayReader); + Debug.Assert(imported != null); imported.PerformRead(s => { From 50afd48812aaf7e4deb437855202836280c93339 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Apr 2024 23:04:56 +0800 Subject: [PATCH 275/581] Add manual test coverage of storyboard videos --- .../TestSceneDrawableStoryboardSprite.cs | 98 ++++++++++++++----- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 32693c2bb2..6ac112cc5f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.IO.Stores; using osu.Framework.Testing; @@ -40,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("disallow all lookups", () => { storyboard.UseSkinSprites = false; - storyboard.AlwaysProvideTexture = false; + storyboard.ProvideResources = false; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow storyboard lookup", () => { storyboard.UseSkinSprites = false; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -67,13 +68,47 @@ namespace osu.Game.Tests.Visual.Gameplay assertStoryboardSourced(); } + [TestCase(false)] + [TestCase(true)] + public void TestVideo(bool scaleTransformProvided) + { + AddStep("allow storyboard lookup", () => + { + storyboard.ProvideResources = true; + }); + + AddStep("create video", () => SetContents(_ => + { + var layer = storyboard.GetLayer("Video"); + + var sprite = new StoryboardVideo("Videos/test-video.mp4", Time.Current); + + sprite.AddLoop(Time.Current, 100).Alpha.Add(Easing.None, 0, 10000, 1, 1); + + if (scaleTransformProvided) + sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current, 1, 1); + + layer.Elements.Clear(); + layer.Add(sprite); + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + storyboard.CreateDrawable() + } + }; + })); + } + [Test] public void TestSkinLookupPreferredOverStoryboard() { AddStep("allow all lookups", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -91,7 +126,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow skin lookup", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = false; + storyboard.ProvideResources = false; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -109,7 +144,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow all lookups", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -127,7 +162,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow all lookups", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -142,7 +177,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow all lookups", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -156,7 +191,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("allow all lookups", () => { storyboard.UseSkinSprites = true; - storyboard.AlwaysProvideTexture = true; + storyboard.ProvideResources = true; }); AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero))); @@ -170,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("origin back", () => sprites.All(s => s.Origin == Anchor.TopLeft)); } - private DrawableStoryboard createSprite(string lookupName, Anchor origin, Vector2 initialPosition) + private Drawable createSprite(string lookupName, Anchor origin, Vector2 initialPosition) { var layer = storyboard.GetLayer("Background"); @@ -180,7 +215,14 @@ namespace osu.Game.Tests.Visual.Gameplay layer.Elements.Clear(); layer.Add(sprite); - return storyboard.CreateDrawable().With(s => s.RelativeSizeAxes = Axes.Both); + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + storyboard.CreateDrawable() + } + }; } private void assertStoryboardSourced() @@ -202,42 +244,52 @@ namespace osu.Game.Tests.Visual.Gameplay return new TestDrawableStoryboard(this, mods); } - public bool AlwaysProvideTexture { get; set; } + public bool ProvideResources { get; set; } - public override string GetStoragePathFromStoryboardPath(string path) => AlwaysProvideTexture ? path : string.Empty; + public override string GetStoragePathFromStoryboardPath(string path) => ProvideResources ? path : string.Empty; private partial class TestDrawableStoryboard : DrawableStoryboard { - private readonly bool alwaysProvideTexture; + private readonly bool provideResources; public TestDrawableStoryboard(TestStoryboard storyboard, IReadOnlyList? mods) : base(storyboard, mods) { - alwaysProvideTexture = storyboard.AlwaysProvideTexture; + provideResources = storyboard.ProvideResources; } - protected override IResourceStore CreateResourceLookupStore() => alwaysProvideTexture - ? new AlwaysReturnsTextureStore() + protected override IResourceStore CreateResourceLookupStore() => provideResources + ? new ResourcesTextureStore() : new ResourceStore(); - internal class AlwaysReturnsTextureStore : IResourceStore + internal class ResourcesTextureStore : IResourceStore { - private const string test_image = "Resources/Textures/test-image.png"; - private readonly DllResourceStore store; - public AlwaysReturnsTextureStore() + public ResourcesTextureStore() { store = TestResources.GetStore(); } public void Dispose() => store.Dispose(); - public byte[] Get(string name) => store.Get(test_image); + public byte[] Get(string name) => store.Get(map(name)); - public Task GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(test_image, cancellationToken); + public Task GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(map(name), cancellationToken); - public Stream GetStream(string name) => store.GetStream(test_image); + public Stream GetStream(string name) => store.GetStream(map(name)); + + private string map(string name) + { + switch (name) + { + case lookup_name: + return "Resources/Textures/test-image.png"; + + default: + return $"Resources/{name}"; + } + } public IEnumerable GetAvailableResources() => store.GetAvailableResources(); } From 7eeac0f3b90d34d71a8e2d7390b4a0310b89288b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 17:34:52 +0800 Subject: [PATCH 276/581] Fix incorrect xmldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index ca2c7b774c..848699a4d2 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -21,7 +21,7 @@ namespace osu.Game.Storyboards.Drawables { Video = video; - // In osu-stable, a mapper can add a scale command for a storyboard. + // In osu-stable, a mapper can add a scale command for a storyboard video. // This allows scaling based on the video's absolute size. // // If not specified we take up the full available space. From 17ca29c2c6e0e5f141f5393923a511cfa2d61437 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 17:37:37 +0800 Subject: [PATCH 277/581] Actually apply transforms to the video --- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 848699a4d2..f2454be190 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -50,6 +50,8 @@ namespace osu.Game.Storyboards.Drawables Origin = Anchor.Centre, Alpha = 0, }; + + Video.ApplyTransforms(drawableVideo); } protected override void LoadComplete() From a978518a74c362016d2aeb39d4a8f1fdcb78a28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 23 Apr 2024 12:32:52 +0200 Subject: [PATCH 278/581] Add failing tests --- .../Settings/TestSceneKeyBindingPanel.cs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 57c9770c9a..86008a56a4 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -296,7 +296,7 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestBindingConflictResolvedByRollback() + public void TestBindingConflictResolvedByRollbackViaMouse() { AddStep("reset taiko section to default", () => { @@ -315,7 +315,7 @@ namespace osu.Game.Tests.Visual.Settings } [Test] - public void TestBindingConflictResolvedByOverwrite() + public void TestBindingConflictResolvedByOverwriteViaMouse() { AddStep("reset taiko section to default", () => { @@ -333,6 +333,46 @@ namespace osu.Game.Tests.Visual.Settings checkBinding("Left (rim)", "M1"); } + [Test] + public void TestBindingConflictResolvedByRollbackViaKeyboard() + { + AddStep("reset taiko & global sections to default", () => + { + panel.ChildrenOfType().First(section => new TaikoRuleset().RulesetInfo.Equals(section.Ruleset)) + .ChildrenOfType().Single().TriggerClick(); + + panel.ChildrenOfType().First().TriggerClick(); + }); + AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); + scrollToAndStartBinding("Left (rim)"); + AddStep("attempt to bind M1 to two keys", () => InputManager.Click(MouseButton.Left)); + + AddUntilStep("wait for popover", () => panel.ChildrenOfType().SingleOrDefault(), () => Is.Not.Null); + AddStep("press Esc", () => InputManager.Key(Key.Escape)); + checkBinding("Left (centre)", "M1"); + checkBinding("Left (rim)", "M2"); + } + + [Test] + public void TestBindingConflictResolvedByOverwriteViaKeyboard() + { + AddStep("reset taiko & global sections to default", () => + { + panel.ChildrenOfType().First(section => new TaikoRuleset().RulesetInfo.Equals(section.Ruleset)) + .ChildrenOfType().Single().TriggerClick(); + + panel.ChildrenOfType().First().TriggerClick(); + }); + AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); + scrollToAndStartBinding("Left (rim)"); + AddStep("attempt to bind M1 to two keys", () => InputManager.Click(MouseButton.Left)); + + AddUntilStep("wait for popover", () => panel.ChildrenOfType().SingleOrDefault(), () => Is.Not.Null); + AddStep("press Enter", () => InputManager.Key(Key.Enter)); + checkBinding("Left (centre)", InputSettingsStrings.ActionHasNoKeyBinding.ToString()); + checkBinding("Left (rim)", "M1"); + } + [Test] public void TestBindingConflictCausedByResetToDefaultOfSingleRow() { From 1e0db1ab9f676ffb781afe7d151c2d6d95126329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 23 Apr 2024 12:44:16 +0200 Subject: [PATCH 279/581] Allow confirming keybinding overwrite on conflict via "select" binding --- .../Sections/Input/KeyBindingConflictPopover.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs index 60d1bd31be..05aeb0b810 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs @@ -152,6 +152,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input newPreview.IsChosen.Value = applyNewButton.IsHovered; } + public override bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == GlobalAction.Select && !e.Repeat) + { + applyNew(); + return true; + } + + return base.OnPressed(e); + } + private partial class ConflictingKeyBindingPreview : CompositeDrawable { private readonly object action; From 564dec7a142b8fe3bf6729b9223bc490df18b5c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 19:21:55 +0800 Subject: [PATCH 280/581] Add test coverage of transforms actually being applied to video --- .../Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index 6ac112cc5f..fc52d749b3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -86,7 +86,10 @@ namespace osu.Game.Tests.Visual.Gameplay sprite.AddLoop(Time.Current, 100).Alpha.Add(Easing.None, 0, 10000, 1, 1); if (scaleTransformProvided) - sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current, 1, 1); + { + sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current + 1000, 1, 2); + sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current + 1000, Time.Current + 2000, 2, 1); + } layer.Elements.Clear(); layer.Add(sprite); From f7626aba1821f5f346dfa36a06438836bbc819ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 23 Apr 2024 13:36:12 +0200 Subject: [PATCH 281/581] Fix mod select overlay columns not displaying properly sometimes Closes https://github.com/ppy/osu/issues/26504. As far as I can tell the issue is basically another manifestation of https://github.com/ppy/osu-framework/issues/5129, i.e. presence overrides causing dropped invalidations and thus completely bogus hierarchy state. The fix is to raise the appropriate invalidation manually. --- osu.Game/Overlays/Mods/ModColumn.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index df33c78ea4..e9f21338bd 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -69,6 +69,7 @@ namespace osu.Game.Overlays.Mods private Task? latestLoadTask; private ModPanel[]? latestLoadedPanels; internal bool ItemsLoaded => latestLoadTask?.IsCompleted == true && allPanelsLoaded; + private bool? wasPresent; private bool allPanelsLoaded { @@ -192,6 +193,15 @@ namespace osu.Game.Overlays.Mods { base.Update(); + // we override `IsPresent` to include the scheduler's pending task state to make async loads work correctly when columns are masked away + // (see description of https://github.com/ppy/osu/pull/19783). + // however, because of that we must also ensure that we signal correct invalidations (https://github.com/ppy/osu-framework/issues/5129). + // failing to do so causes columns to be stuck in "present" mode despite actually not being present themselves. + // this works because `Update()` will always run after a scheduler update, which is what causes the presence state change responsible for the failure. + if (wasPresent != null && wasPresent != IsPresent) + Invalidate(Invalidation.Presence); + wasPresent = IsPresent; + if (selectionDelay == initial_multiple_selection_delay || Time.Current - lastSelection >= selectionDelay) { if (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) From 804b1b0d884da13b1a79a31ffd09e23a16bffbe8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 20:54:44 +0800 Subject: [PATCH 282/581] Fix settings colour scheme wrong when viewing gameplay from skin editor button Closes https://github.com/ppy/osu/issues/27949. --- osu.Game/Screens/Play/Player.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 3a80caf259..42ff1d74f3 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -101,6 +101,11 @@ namespace osu.Game.Screens.Play /// public IBindable ShowingOverlayComponents = new Bindable(); + // Should match PlayerLoader for consistency. Cached here for the rare case we push a Player + // without the loading screen (one such usage is the skin editor's scene library). + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + [Resolved] private ScoreManager scoreManager { get; set; } From 436203a8c12093c076a9d4e5d01264ad9ce26f55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 21:32:32 +0800 Subject: [PATCH 283/581] Add a chevron to distinguish editor menus with submenus --- .../Edit/Components/Menus/EditorMenuBar.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index 0e125d0ec0..152bcee214 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -184,6 +185,17 @@ namespace osu.Game.Screens.Edit.Components.Menus { } + private bool hasSubmenu => Item.Items.Any(); + + protected override TextContainer CreateTextContainer() => base.CreateTextContainer().With(c => + { + c.Padding = new MarginPadding + { + // Add some padding for the chevron below. + Right = hasSubmenu ? 5 : 0, + }; + }); + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -191,6 +203,18 @@ namespace osu.Game.Screens.Edit.Components.Menus BackgroundColourHover = colourProvider.Background1; Foreground.Padding = new MarginPadding { Vertical = 2 }; + + if (hasSubmenu) + { + AddInternal(new SpriteIcon + { + Margin = new MarginPadding(6), + Size = new Vector2(8), + Icon = FontAwesome.Solid.ChevronRight, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }); + } } } } From 602b16f533dd5e578025144d025dc3a9c7336fa7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 22:03:32 +0800 Subject: [PATCH 284/581] Fix fade-in no longer working on videos --- .../Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs | 2 -- osu.Game/Storyboards/StoryboardVideo.cs | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index fc52d749b3..8fa2c9922e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -83,8 +83,6 @@ namespace osu.Game.Tests.Visual.Gameplay var sprite = new StoryboardVideo("Videos/test-video.mp4", Time.Current); - sprite.AddLoop(Time.Current, 100).Alpha.Add(Easing.None, 0, 10000, 1, 1); - if (scaleTransformProvided) { sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current + 1000, 1, 2); diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index bd1b933b6f..5573162d26 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -12,7 +12,9 @@ namespace osu.Game.Storyboards public StoryboardVideo(string path, double offset) : base(path, Anchor.Centre, Vector2.Zero) { - TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 1); + // This is just required to get a valid StartTime based on the incoming offset. + // Actual fades are handled inside DrawableStoryboardVideo for now. + TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 0); } public override Drawable CreateDrawable() => new DrawableStoryboardVideo(this); From d0edf72a0c384004f7e6fbb252d368af3fa14cdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Apr 2024 22:04:28 +0800 Subject: [PATCH 285/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c61977cfa3..97dfe5d9f7 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 6389172fe7..66347acdf0 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 787e60f70649d5346c1916332ff74cab0518f959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 23 Apr 2024 18:37:15 +0200 Subject: [PATCH 286/581] Fix `LegacyDrainingHealthProcessor` drain rate computation diverging to infinity --- osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs index 7cee5ebecf..25c5b3643a 100644 --- a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs @@ -129,6 +129,13 @@ namespace osu.Game.Rulesets.Scoring OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); } + if (!fail && double.IsInfinity(HpMultiplierNormal)) + { + OnIterationSuccess?.Invoke("Drain computation algorithm diverged to infinity. PASSING with zero drop, resetting HP multiplier to 1."); + HpMultiplierNormal = 1; + return 0; + } + if (!fail) { OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); From cbbf2dd1584da084293db3afe4bd4e6108e5f7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 23 Apr 2024 18:58:40 +0200 Subject: [PATCH 287/581] Fix `DifficultyBindable` bound desync between `maxValue` and `CurrentNumber.MaxValue` --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index a207048882..5f6fd21860 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mods } } - private float maxValue; + private float maxValue = 10; // matches default max value of `CurrentNumber` public float MaxValue { From 16fdd4e08d81cfc00ddc1a6d370fdece195c3461 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 Apr 2024 09:01:31 +0800 Subject: [PATCH 288/581] Add ability to show beatmap source using skin editor's beatmap attribute text As per https://github.com/ppy/osu/discussions/27955. --- osu.Game/Localisation/EditorSetupStrings.cs | 32 +++++++++++++------ .../Components/BeatmapAttributeText.cs | 3 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/osu.Game/Localisation/EditorSetupStrings.cs b/osu.Game/Localisation/EditorSetupStrings.cs index eff6f9e6b8..350517734f 100644 --- a/osu.Game/Localisation/EditorSetupStrings.cs +++ b/osu.Game/Localisation/EditorSetupStrings.cs @@ -42,7 +42,8 @@ namespace osu.Game.Localisation /// /// "If enabled, an "Are you ready? 3, 2, 1, GO!" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so." /// - public static LocalisableString CountdownDescription => new TranslatableString(getKey(@"countdown_description"), @"If enabled, an ""Are you ready? 3, 2, 1, GO!"" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so."); + public static LocalisableString CountdownDescription => new TranslatableString(getKey(@"countdown_description"), + @"If enabled, an ""Are you ready? 3, 2, 1, GO!"" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so."); /// /// "Countdown speed" @@ -52,7 +53,8 @@ namespace osu.Game.Localisation /// /// "If the countdown sounds off-time, use this to make it appear one or more beats early." /// - public static LocalisableString CountdownOffsetDescription => new TranslatableString(getKey(@"countdown_offset_description"), @"If the countdown sounds off-time, use this to make it appear one or more beats early."); + public static LocalisableString CountdownOffsetDescription => + new TranslatableString(getKey(@"countdown_offset_description"), @"If the countdown sounds off-time, use this to make it appear one or more beats early."); /// /// "Countdown offset" @@ -67,7 +69,8 @@ namespace osu.Game.Localisation /// /// "Allows storyboards to use the full screen space, rather than be confined to a 4:3 area." /// - public static LocalisableString WidescreenSupportDescription => new TranslatableString(getKey(@"widescreen_support_description"), @"Allows storyboards to use the full screen space, rather than be confined to a 4:3 area."); + public static LocalisableString WidescreenSupportDescription => + new TranslatableString(getKey(@"widescreen_support_description"), @"Allows storyboards to use the full screen space, rather than be confined to a 4:3 area."); /// /// "Epilepsy warning" @@ -77,7 +80,8 @@ namespace osu.Game.Localisation /// /// "Recommended if the storyboard or video contain scenes with rapidly flashing colours." /// - public static LocalisableString EpilepsyWarningDescription => new TranslatableString(getKey(@"epilepsy_warning_description"), @"Recommended if the storyboard or video contain scenes with rapidly flashing colours."); + public static LocalisableString EpilepsyWarningDescription => + new TranslatableString(getKey(@"epilepsy_warning_description"), @"Recommended if the storyboard or video contain scenes with rapidly flashing colours."); /// /// "Letterbox during breaks" @@ -87,7 +91,8 @@ namespace osu.Game.Localisation /// /// "Adds horizontal letterboxing to give a cinematic look during breaks." /// - public static LocalisableString LetterboxDuringBreaksDescription => new TranslatableString(getKey(@"letterbox_during_breaks_description"), @"Adds horizontal letterboxing to give a cinematic look during breaks."); + public static LocalisableString LetterboxDuringBreaksDescription => + new TranslatableString(getKey(@"letterbox_during_breaks_description"), @"Adds horizontal letterboxing to give a cinematic look during breaks."); /// /// "Samples match playback rate" @@ -97,7 +102,8 @@ namespace osu.Game.Localisation /// /// "When enabled, all samples will speed up or slow down when rate-changing mods are enabled." /// - public static LocalisableString SamplesMatchPlaybackRateDescription => new TranslatableString(getKey(@"samples_match_playback_rate_description"), @"When enabled, all samples will speed up or slow down when rate-changing mods are enabled."); + public static LocalisableString SamplesMatchPlaybackRateDescription => new TranslatableString(getKey(@"samples_match_playback_rate_description"), + @"When enabled, all samples will speed up or slow down when rate-changing mods are enabled."); /// /// "The size of all hit objects" @@ -117,7 +123,8 @@ namespace osu.Game.Localisation /// /// "The harshness of hit windows and difficulty of special objects (ie. spinners)" /// - public static LocalisableString OverallDifficultyDescription => new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)"); + public static LocalisableString OverallDifficultyDescription => + new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)"); /// /// "Tick Rate" @@ -127,7 +134,8 @@ namespace osu.Game.Localisation /// /// "Determines how many "ticks" are generated within long hit objects. A tick rate of 1 will generate ticks on each beat, 2 would be twice per beat, etc." /// - public static LocalisableString TickRateDescription => new TranslatableString(getKey(@"tick_rate_description"), @"Determines how many ""ticks"" are generated within long hit objects. A tick rate of 1 will generate ticks on each beat, 2 would be twice per beat, etc."); + public static LocalisableString TickRateDescription => new TranslatableString(getKey(@"tick_rate_description"), + @"Determines how many ""ticks"" are generated within long hit objects. A tick rate of 1 will generate ticks on each beat, 2 would be twice per beat, etc."); /// /// "Base Velocity" @@ -137,7 +145,8 @@ namespace osu.Game.Localisation /// /// "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."); + 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" @@ -159,6 +168,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Creator => new TranslatableString(getKey(@"creator"), @"Creator"); + /// + /// "Source" + /// + public static LocalisableString Source => new TranslatableString(getKey(@"source"), @"Source"); + /// /// "Difficulty Name" /// diff --git a/osu.Game/Skinning/Components/BeatmapAttributeText.cs b/osu.Game/Skinning/Components/BeatmapAttributeText.cs index 52c439a624..5c5e509fb2 100644 --- a/osu.Game/Skinning/Components/BeatmapAttributeText.cs +++ b/osu.Game/Skinning/Components/BeatmapAttributeText.cs @@ -48,6 +48,7 @@ namespace osu.Game.Skinning.Components [BeatmapAttribute.Artist] = EditorSetupStrings.Artist, [BeatmapAttribute.DifficultyName] = EditorSetupStrings.DifficultyHeader, [BeatmapAttribute.Creator] = EditorSetupStrings.Creator, + [BeatmapAttribute.Source] = EditorSetupStrings.Source, [BeatmapAttribute.Length] = ArtistStrings.TracklistLength.ToTitle(), [BeatmapAttribute.RankedStatus] = BeatmapDiscussionsStrings.IndexFormBeatmapsetStatusDefault, [BeatmapAttribute.BPM] = BeatmapsetsStrings.ShowStatsBpm, @@ -88,6 +89,7 @@ namespace osu.Game.Skinning.Components valueDictionary[BeatmapAttribute.Artist] = new RomanisableString(workingBeatmap.BeatmapInfo.Metadata.ArtistUnicode, workingBeatmap.BeatmapInfo.Metadata.Artist); valueDictionary[BeatmapAttribute.DifficultyName] = workingBeatmap.BeatmapInfo.DifficultyName; valueDictionary[BeatmapAttribute.Creator] = workingBeatmap.BeatmapInfo.Metadata.Author.Username; + valueDictionary[BeatmapAttribute.Source] = workingBeatmap.BeatmapInfo.Metadata.Source; valueDictionary[BeatmapAttribute.Length] = TimeSpan.FromMilliseconds(workingBeatmap.BeatmapInfo.Length).ToFormattedDuration(); valueDictionary[BeatmapAttribute.RankedStatus] = workingBeatmap.BeatmapInfo.Status.GetLocalisableDescription(); valueDictionary[BeatmapAttribute.BPM] = workingBeatmap.BeatmapInfo.BPM.ToLocalisableString(@"F2"); @@ -132,6 +134,7 @@ namespace osu.Game.Skinning.Components StarRating, Title, Artist, + Source, DifficultyName, Creator, Length, From f97c519451e0b17c95309f2d78bbddd30614e5c9 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 24 Apr 2024 00:19:10 -0700 Subject: [PATCH 289/581] Add chevron to distinguish all menus with submenus --- .../UserInterface/DrawableOsuMenuItem.cs | 23 +++++++++++++++++++ .../Edit/Components/Menus/EditorMenuBar.cs | 23 ------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs index 2f2cb7e5f8..06ef75cf58 100644 --- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osuTK; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -40,6 +42,27 @@ namespace osu.Game.Graphics.UserInterface AddInternal(hoverClickSounds = new HoverClickSounds()); updateTextColour(); + + bool hasSubmenu = Item.Items.Any(); + + // Only add right chevron if direction of menu items is vertical (i.e. width is relative size, see `DrawableMenuItem.SetFlowDirection()`). + if (hasSubmenu && RelativeSizeAxes == Axes.X) + { + AddInternal(new SpriteIcon + { + Margin = new MarginPadding(6), + Size = new Vector2(8), + Icon = FontAwesome.Solid.ChevronRight, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }); + + text.Padding = new MarginPadding + { + // Add some padding for the chevron above. + Right = 5, + }; + } } protected override void LoadComplete() diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index 152bcee214..c410c2519b 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -185,17 +185,6 @@ namespace osu.Game.Screens.Edit.Components.Menus { } - private bool hasSubmenu => Item.Items.Any(); - - protected override TextContainer CreateTextContainer() => base.CreateTextContainer().With(c => - { - c.Padding = new MarginPadding - { - // Add some padding for the chevron below. - Right = hasSubmenu ? 5 : 0, - }; - }); - [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -203,18 +192,6 @@ namespace osu.Game.Screens.Edit.Components.Menus BackgroundColourHover = colourProvider.Background1; Foreground.Padding = new MarginPadding { Vertical = 2 }; - - if (hasSubmenu) - { - AddInternal(new SpriteIcon - { - Margin = new MarginPadding(6), - Size = new Vector2(8), - Icon = FontAwesome.Solid.ChevronRight, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - }); - } } } } From 5f463b81a8ecc883ed8040cacf70139e11b042dd Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 24 Apr 2024 00:22:20 -0700 Subject: [PATCH 290/581] Remove hardcoded chevrons in test --- osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs index 7b80549854..2a2f267fc8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneContextMenu.cs @@ -77,21 +77,21 @@ namespace osu.Game.Tests.Visual.UserInterface new OsuMenuItem(@"Some option"), new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted), new OsuMenuItem(@"Another option"), - new OsuMenuItem(@"Nested option >") + new OsuMenuItem(@"Nested option") { Items = new MenuItem[] { new OsuMenuItem(@"Sub-One"), new OsuMenuItem(@"Sub-Two"), new OsuMenuItem(@"Sub-Three"), - new OsuMenuItem(@"Sub-Nested option >") + new OsuMenuItem(@"Sub-Nested option") { Items = new MenuItem[] { new OsuMenuItem(@"Double Sub-One"), new OsuMenuItem(@"Double Sub-Two"), new OsuMenuItem(@"Double Sub-Three"), - new OsuMenuItem(@"Sub-Sub-Nested option >") + new OsuMenuItem(@"Sub-Sub-Nested option") { Items = new MenuItem[] { From 4f7c9f297068b5ec27aaa3d34e8176d6e0e7fe1a Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 24 Apr 2024 01:00:03 -0700 Subject: [PATCH 291/581] Remove unused using --- osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index c410c2519b..0e125d0ec0 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From 72726809cb6cdd53fb3f01d13b2438d323c47e72 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 24 Apr 2024 09:47:41 -0700 Subject: [PATCH 292/581] Ignore autogenerated .idea android file --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 525b3418cd..11fee27f28 100644 --- a/.gitignore +++ b/.gitignore @@ -340,4 +340,5 @@ inspectcode # Fody (pulled in by Realm) - schema file FodyWeavers.xsd -.idea/.idea.osu.Desktop/.idea/misc.xml \ No newline at end of file +.idea/.idea.osu.Desktop/.idea/misc.xml +.idea/.idea.osu.Android/.idea/deploymentTargetDropDown.xml From 94275f148e4eb523fbf4247503c7e072f66ef780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 09:01:47 +0200 Subject: [PATCH 293/581] Fix adding slider control points via context menu not undoing correctly Closes https://github.com/ppy/osu/issues/27985. --- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 2da462caf4..49fdf12d60 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -403,7 +403,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders public override MenuItem[] ContextMenuItems => new MenuItem[] { - new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)), + new OsuMenuItem("Add control point", MenuItemType.Standard, () => + { + changeHandler?.BeginChange(); + addControlPoint(rightClickPosition); + changeHandler?.EndChange(); + }), new OsuMenuItem("Convert to stream", MenuItemType.Destructive, convertToStream), }; From da953b34a721540e266b33d495ce11525bf1c8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 09:52:26 +0200 Subject: [PATCH 294/581] Apply nullability annotations to `ResultsScreen` & inheritors --- .../Visual/Ranking/TestSceneResultsScreen.cs | 2 +- .../MultiplayerTeamResultsScreen.cs | 8 ++-- .../Spectate/MultiSpectatorResultsScreen.cs | 6 +-- .../Playlists/PlaylistsResultsScreen.cs | 35 +++++++--------- osu.Game/Screens/Ranking/ResultsScreen.cs | 42 +++++++++---------- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 4 +- 6 files changed, 43 insertions(+), 54 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index ffc5dbc8fb..fca1d0f82a 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -424,7 +424,7 @@ namespace osu.Game.Tests.Visual.Ranking scores.Add(score); } - scoresCallback?.Invoke(scores); + scoresCallback.Invoke(scores); return null; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs index a8c513603c..ab83860ba7 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerTeamResultsScreen.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -28,8 +26,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { private readonly SortedDictionary teamScores; - private Container winnerBackground; - private Drawable winnerText; + private Container winnerBackground = null!; + private Drawable winnerText = null!; public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary teamScores) : base(score, roomId, playlistItem) @@ -41,7 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs index 2afc187e40..c240bbea0c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using osu.Game.Online.API; @@ -25,8 +23,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate Scheduler.AddDelayed(() => StatisticsPanel.ToggleVisibility(), 1000); } - protected override APIRequest FetchScores(Action> scoresCallback) => null; + protected override APIRequest? FetchScores(Action> scoresCallback) => null; - protected override APIRequest FetchNextPage(int direction, Action> scoresCallback) => null; + protected override APIRequest? FetchNextPage(int direction, Action> scoresCallback) => null; } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs index add7aee8cd..fdb83b5ae8 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs @@ -1,13 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -25,23 +22,23 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private readonly long roomId; private readonly PlaylistItem playlistItem; - protected LoadingSpinner LeftSpinner { get; private set; } - protected LoadingSpinner CentreSpinner { get; private set; } - protected LoadingSpinner RightSpinner { get; private set; } + protected LoadingSpinner LeftSpinner { get; private set; } = null!; + protected LoadingSpinner CentreSpinner { get; private set; } = null!; + protected LoadingSpinner RightSpinner { get; private set; } = null!; - private MultiplayerScores higherScores; - private MultiplayerScores lowerScores; + private MultiplayerScores? higherScores; + private MultiplayerScores? lowerScores; [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; [Resolved] - private ScoreManager scoreManager { get; set; } + private ScoreManager scoreManager { get; set; } = null!; [Resolved] - private RulesetStore rulesets { get; set; } + private RulesetStore rulesets { get; set; } = null!; - public PlaylistsResultsScreen([CanBeNull] ScoreInfo score, long roomId, PlaylistItem playlistItem) + public PlaylistsResultsScreen(ScoreInfo? score, long roomId, PlaylistItem playlistItem) : base(score) { this.roomId = roomId; @@ -123,11 +120,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return userScoreReq; } - protected override APIRequest FetchNextPage(int direction, Action> scoresCallback) + protected override APIRequest? FetchNextPage(int direction, Action> scoresCallback) { Debug.Assert(direction == 1 || direction == -1); - MultiplayerScores pivot = direction == -1 ? higherScores : lowerScores; + MultiplayerScores? pivot = direction == -1 ? higherScores : lowerScores; if (pivot?.Cursor == null) return null; @@ -147,7 +144,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// The callback to perform with the resulting scores. /// An optional score pivot to retrieve scores around. Can be null to retrieve scores from the highest score. /// The indexing . - private APIRequest createIndexRequest(Action> scoresCallback, [CanBeNull] MultiplayerScores pivot = null) + private APIRequest createIndexRequest(Action> scoresCallback, MultiplayerScores? pivot = null) { var indexReq = pivot != null ? new IndexPlaylistScoresRequest(roomId, playlistItem.ID, pivot.Cursor, pivot.Params) @@ -180,7 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// The callback to invoke with the final s. /// The s that were retrieved from s. /// An optional pivot around which the scores were retrieved. - private void performSuccessCallback([NotNull] Action> callback, [NotNull] List scores, [CanBeNull] MultiplayerScores pivot = null) => Schedule(() => + private void performSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null) => Schedule(() => { var scoreInfos = scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray(); @@ -201,7 +198,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists hideLoadingSpinners(pivot); }); - private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null) + private void hideLoadingSpinners(MultiplayerScores? pivot = null) { CentreSpinner.Hide(); @@ -217,7 +214,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// The to set positions on. /// The pivot. /// The amount to increment the pivot position by for each in . - private void setPositions([NotNull] MultiplayerScores scores, [CanBeNull] MultiplayerScores pivot, int increment) + private void setPositions(MultiplayerScores scores, MultiplayerScores? pivot, int increment) => setPositions(scores, pivot?.Scores[^1].Position ?? 0, increment); /// @@ -226,7 +223,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// The to set positions on. /// The pivot position. /// The amount to increment the pivot position by for each in . - private void setPositions([NotNull] MultiplayerScores scores, int pivotPosition, int increment) + private void setPositions(MultiplayerScores scores, int pivotPosition, int increment) { foreach (var s in scores.Scores) { diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index ebb0530046..1c3518909d 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -1,12 +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 disable - using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -45,25 +42,24 @@ namespace osu.Game.Screens.Ranking protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; - public readonly Bindable SelectedScore = new Bindable(); + public readonly Bindable SelectedScore = new Bindable(); - [CanBeNull] - public readonly ScoreInfo Score; + public readonly ScoreInfo? Score; - protected ScorePanelList ScorePanelList { get; private set; } + protected ScorePanelList ScorePanelList { get; private set; } = null!; - protected VerticalScrollContainer VerticalScrollContent { get; private set; } - - [Resolved(CanBeNull = true)] - private Player player { get; set; } + protected VerticalScrollContainer VerticalScrollContent { get; private set; } = null!; [Resolved] - private IAPIProvider api { get; set; } + private Player? player { get; set; } - protected StatisticsPanel StatisticsPanel { get; private set; } + [Resolved] + private IAPIProvider api { get; set; } = null!; - private Drawable bottomPanel; - private Container detachedPanelContainer; + protected StatisticsPanel StatisticsPanel { get; private set; } = null!; + + private Drawable bottomPanel = null!; + private Container detachedPanelContainer = null!; private bool lastFetchCompleted; @@ -84,9 +80,9 @@ namespace osu.Game.Screens.Ranking /// public bool ShowUserStatistics { get; init; } - private Sample popInSample; + private Sample? popInSample; - protected ResultsScreen([CanBeNull] ScoreInfo score) + protected ResultsScreen(ScoreInfo? score) { Score = score; @@ -182,11 +178,11 @@ namespace osu.Game.Screens.Ranking Scheduler.AddDelayed(() => OverlayActivationMode.Value = OverlayActivation.All, shouldFlair ? AccuracyCircle.TOTAL_DURATION + 1000 : 0); } - if (AllowWatchingReplay) + if (SelectedScore.Value != null && AllowWatchingReplay) { buttons.Add(new ReplayDownloadButton(SelectedScore.Value) { - Score = { BindTarget = SelectedScore }, + Score = { BindTarget = SelectedScore! }, Width = 300 }); } @@ -225,7 +221,7 @@ namespace osu.Game.Screens.Ranking if (lastFetchCompleted) { - APIRequest nextPageRequest = null; + APIRequest? nextPageRequest = null; if (ScorePanelList.IsScrolledToStart) nextPageRequest = FetchNextPage(-1, fetchScoresCallback); @@ -245,7 +241,7 @@ namespace osu.Game.Screens.Ranking /// /// A callback which should be called when fetching is completed. Scheduling is not required. /// An responsible for the fetch operation. This will be queued and performed automatically. - protected virtual APIRequest FetchScores(Action> scoresCallback) => null; + protected virtual APIRequest? FetchScores(Action> scoresCallback) => null; /// /// Performs a fetch of the next page of scores. This is invoked every frame until a non-null is returned. @@ -253,7 +249,7 @@ namespace osu.Game.Screens.Ranking /// The fetch direction. -1 to fetch scores greater than the current start of the list, and 1 to fetch scores lower than the current end of the list. /// A callback which should be called when fetching is completed. Scheduling is not required. /// An responsible for the fetch operation. This will be queued and performed automatically. - protected virtual APIRequest FetchNextPage(int direction, Action> scoresCallback) => null; + protected virtual APIRequest? FetchNextPage(int direction, Action> scoresCallback) => null; /// /// Creates the to be used to display extended information about scores. @@ -327,7 +323,7 @@ namespace osu.Game.Screens.Ranking panel.Alpha = 0; } - private ScorePanel detachedPanel; + private ScorePanel? detachedPanel; private void onStatisticsStateChanged(ValueChangedEvent state) { diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index ee0251b5ac..33b4bf976b 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Ranking { } - protected override APIRequest? FetchScores(Action>? scoresCallback) + protected override APIRequest? FetchScores(Action> scoresCallback) { Debug.Assert(Score != null); @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Ranking return null; getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset); - getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => !s.MatchesOnlineID(Score)).Select(s => s.ToScoreInfo(rulesets, Beatmap.Value.BeatmapInfo))); + getScoreRequest.Success += r => scoresCallback.Invoke(r.Scores.Where(s => !s.MatchesOnlineID(Score)).Select(s => s.ToScoreInfo(rulesets, Beatmap.Value.BeatmapInfo))); return getScoreRequest; } From 9e919b784d99e80371bcdab948eeb80346b4892a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 11:19:29 +0200 Subject: [PATCH 295/581] Add test case covering ignoring non-basic results --- .../Ranking/TestSceneHitEventTimingDistributionGraph.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index 325a535731..3e38b66029 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -82,6 +82,14 @@ namespace osu.Game.Tests.Visual.Ranking }).ToList()); } + [Test] + public void TestNonBasicHitResultsAreIgnored() + { + createTest(CreateDistributedHitEvents(0, 50) + .Select(h => new HitEvent(h.TimeOffset, 1.0, h.TimeOffset > 0 ? HitResult.Ok : HitResult.LargeTickHit, placeholder_object, placeholder_object, null)) + .ToList()); + } + [Test] public void TestMultipleWindowsOfHitResult() { From b250a924b19687d626737c78dffbd6e692859d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 11:20:07 +0200 Subject: [PATCH 296/581] Do not show non-basic results in timing distribution graph Closes https://github.com/ppy/osu/issues/24274. Bit of an ad-hoc resolution but maybe fine? This basically proposes to bypass the problem described in the issue by just not showing tick hits at all on the distribution graph. --- .../Ranking/Statistics/HitEventTimingDistributionGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 1260ec2339..47807a8346 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Ranking.Statistics /// The s to display the timing distribution of. public HitEventTimingDistributionGraph(IReadOnlyList hitEvents) { - this.hitEvents = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit()).ToList(); + this.hitEvents = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsBasic() && e.Result.IsHit()).ToList(); bins = Enumerable.Range(0, total_timing_distribution_bins).Select(_ => new Dictionary()).ToArray>(); } From d2e9c33b6a34442a0b3e1bead88eac96d20c53e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 12:49:25 +0200 Subject: [PATCH 297/581] Add failing test case --- .../Editing/TestSceneDifficultyDelete.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs index 12e00c4485..0f99270a9b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit; using osu.Game.Storyboards; using osu.Game.Tests.Beatmaps.IO; using osuTK.Input; @@ -83,6 +84,49 @@ namespace osu.Game.Tests.Visual.Editing } } + [Test] + public void TestDeleteDifficultyWithPendingChanges() + { + Guid deletedDifficultyID = Guid.Empty; + int countBeforeDeletion = 0; + string beatmapSetHashBefore = string.Empty; + + AddUntilStep("wait for editor to load", () => Editor?.ReadyForUse == true); + + AddStep("store selected difficulty", () => + { + deletedDifficultyID = EditorBeatmap.BeatmapInfo.ID; + countBeforeDeletion = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count; + beatmapSetHashBefore = Beatmap.Value.BeatmapSetInfo.Hash; + }); + + AddStep("make change to difficulty", () => + { + EditorBeatmap.BeginChange(); + EditorBeatmap.BeatmapInfo.DifficultyName = "changin' things"; + EditorBeatmap.EndChange(); + }); + + AddStep("click File", () => this.ChildrenOfType().First().TriggerClick()); + + AddStep("click delete", () => getDeleteMenuItem().TriggerClick()); + AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog != null); + AddAssert("dialog is deletion confirmation dialog", () => DialogOverlay.CurrentDialog, Is.InstanceOf); + AddStep("confirm", () => InputManager.Key(Key.Number1)); + + AddUntilStep("no next dialog", () => DialogOverlay.CurrentDialog == null); + AddUntilStep("switched to different difficulty", + () => this.ChildrenOfType().SingleOrDefault() != null && EditorBeatmap.BeatmapInfo.ID != deletedDifficultyID); + + AddAssert($"difficulty is unattached from set", + () => Beatmap.Value.BeatmapSetInfo.Beatmaps.Select(b => b.ID), () => Does.Not.Contain(deletedDifficultyID)); + AddAssert("beatmap set difficulty count decreased by one", + () => Beatmap.Value.BeatmapSetInfo.Beatmaps.Count, () => Is.EqualTo(countBeforeDeletion - 1)); + AddAssert("set hash changed", () => Beatmap.Value.BeatmapSetInfo.Hash, () => Is.Not.EqualTo(beatmapSetHashBefore)); + AddAssert($"difficulty is deleted from realm", + () => Realm.Run(r => r.Find(deletedDifficultyID)), () => Is.Null); + } + private DrawableOsuMenuItem getDeleteMenuItem() => this.ChildrenOfType() .Single(item => item.ChildrenOfType().Any(text => text.Text.ToString().StartsWith("Delete", StringComparison.Ordinal))); } From 19d006d8182812710bffd94d26a4fb2512c101cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 12:51:30 +0200 Subject: [PATCH 298/581] Fix deleting modified difficulty via editor leaving user in broken state Closes https://github.com/ppy/osu/issues/22783. If the difficulty being edited has unsaved changes, the editor exit flow would prompt for save *after* the deletion method has run. This is undesirable from a UX standpoint, and also leaves the user in a broken state. Thus, just fake an update of the last saved hash of the beatmap to fool the editor into thinking that it's not dirty, so that the exit flow will not show a save dialog. --- osu.Game/Screens/Edit/Editor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 37f4b4f5be..980c613311 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1088,6 +1088,13 @@ namespace osu.Game.Screens.Edit var difficultiesBeforeDeletion = groupedOrderedBeatmaps.SelectMany(g => g).ToList(); + // if the difficulty being currently deleted has unsaved changes, + // the editor exit flow would prompt for save *after* this method has done its thing. + // this is generally undesirable and also ends up leaving the user in a broken state. + // therefore, just update the last saved hash to make the exit flow think the deleted beatmap is not dirty, + // so that it will not show the save dialog on exit. + updateLastSavedHash(); + beatmapManager.DeleteDifficultyImmediately(difficultyToDelete); int deletedIndex = difficultiesBeforeDeletion.IndexOf(difficultyToDelete); From c1107d2797ae807d33cdefff3edd54459781919d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 Apr 2024 14:31:13 +0200 Subject: [PATCH 299/581] Fully refetch working beatmap when entering editor Closes https://github.com/ppy/osu/issues/21794. I'm not actually super sure as to what the exact mode of failure is here, but it's 99% to do with working beatmap cache invalidation. Likely this can be even considered as another case of https://github.com/ppy/osu/issues/21357, but because this is a one-liner "fix," I'm PRing it anyways. The issue is confusing to understand when working with the swap scenario given in the issue, but it's a little easier to understand when performing the following: 1. Have a beatmap set with 2 difficulties. Let's call them "A" and "B". 2. From song select, without ever exiting to main menu, edit "A". Change the difficulty name to "AA". Save and exit back to song select; do not exit out to main menu. 3. From song select, edit "B". Change the difficulty name to "BB". Save and exit back to song select. 4. The difficulty names will be "A" and "BB". Basically what I *think* is causing this, is the fact that even though editor invalidates the working beatmap by refetching it afresh on exit, song select is blissfully unaware of this, and continues working with its own `BeatmapInfo` instances which have backlinks to `BeatmapSetInfo`. When editing the first of the two difficulties and then the second, the editing of the first one only invalidates the first one rather than the entire set, and the second difficulty continues to have a stale reference to the first one via the beatmap set, and as such ends up overwriting the changes from the first save when passed into the editor and modified again. --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 15469fad5b..16879d0cf0 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -425,7 +425,7 @@ namespace osu.Game.Screens.Select if (!AllowEditing) throw new InvalidOperationException($"Attempted to edit when {nameof(AllowEditing)} is disabled"); - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo ?? beatmapInfoNoDebounce); + Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo ?? beatmapInfoNoDebounce, true); this.Push(new EditorLoader()); } From 1756da0dda45363e89c9e3aaf3a70fb32356977d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Apr 2024 21:14:09 +0800 Subject: [PATCH 300/581] Fix redundant string interpolations --- osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs index 0f99270a9b..d4bd77642c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultyDelete.cs @@ -118,12 +118,12 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("switched to different difficulty", () => this.ChildrenOfType().SingleOrDefault() != null && EditorBeatmap.BeatmapInfo.ID != deletedDifficultyID); - AddAssert($"difficulty is unattached from set", + AddAssert("difficulty is unattached from set", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.Select(b => b.ID), () => Does.Not.Contain(deletedDifficultyID)); AddAssert("beatmap set difficulty count decreased by one", () => Beatmap.Value.BeatmapSetInfo.Beatmaps.Count, () => Is.EqualTo(countBeforeDeletion - 1)); AddAssert("set hash changed", () => Beatmap.Value.BeatmapSetInfo.Hash, () => Is.Not.EqualTo(beatmapSetHashBefore)); - AddAssert($"difficulty is deleted from realm", + AddAssert("difficulty is deleted from realm", () => Realm.Run(r => r.Find(deletedDifficultyID)), () => Is.Null); } From 387fcb87819f15dbaa4c8d10423ea0d6bd01b6db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Apr 2024 21:31:36 +0800 Subject: [PATCH 301/581] Add a brief inline comment to make sure we don't undo the fix --- osu.Game/Screens/Select/SongSelect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 16879d0cf0..6225534e95 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -425,6 +425,7 @@ namespace osu.Game.Screens.Select if (!AllowEditing) throw new InvalidOperationException($"Attempted to edit when {nameof(AllowEditing)} is disabled"); + // Forced refetch is important here to guarantee correct invalidation across all difficulties. Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo ?? beatmapInfoNoDebounce, true); this.Push(new EditorLoader()); } From e0e790fa9412368ff7b414791348476f05e28f2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Apr 2024 14:44:44 +0800 Subject: [PATCH 302/581] Fix a couple of xmldoc typos --- osu.Game/OsuGameBase.cs | 2 +- .../OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fb7a238c46..0122afb239 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -94,7 +94,7 @@ namespace osu.Game public const int SAMPLE_DEBOUNCE_TIME = 20; /// - /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. + /// The maximum volume at which audio tracks should play back at. This can be set lower than 1 to create some head-room for sound effects. /// private const double global_track_volume_adjust = 0.8; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs index fd61b60fe4..5ff52be8bc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate } /// - /// Updates the catchup states of all player clocks clocks. + /// Updates the catchup states of all player clocks. /// private void updatePlayerCatchup() { From 21d65568651a2760891dd3b2c5c6d55c1c99a688 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Apr 2024 15:29:59 +0800 Subject: [PATCH 303/581] Remove managed clocks from `SpectatorSyncManager` on gameplay completion / abort --- .../Multiplayer/Spectate/MultiSpectatorScreen.cs | 13 +++++++++++-- .../Multiplayer/Spectate/SpectatorSyncManager.cs | 1 + osu.Game/Screens/Spectate/SpectatorScreen.cs | 7 +++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index e2159f0e3b..cb00763e6b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -244,10 +244,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate playerArea.LoadScore(spectatorGameplayState.Score); }); - protected override void FailGameplay(int userId) + protected override void FailGameplay(int userId) => Schedule(() => { // We probably want to visualise this in the future. - } + + var instance = instances.Single(i => i.UserId == userId); + syncManager.RemoveManagedClock(instance.SpectatorPlayerClock); + }); + + protected override void PassGameplay(int userId) => Schedule(() => + { + var instance = instances.Single(i => i.UserId == userId); + syncManager.RemoveManagedClock(instance.SpectatorPlayerClock); + }); protected override void QuitGameplay(int userId) => Schedule(() => { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs index 5ff52be8bc..9eb448d9d0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs @@ -76,6 +76,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public void RemoveManagedClock(SpectatorPlayerClock clock) { playerClocks.Remove(clock); + Logger.Log($"Removing managed clock from {nameof(SpectatorSyncManager)} ({playerClocks.Count} remain)"); clock.IsRunning = false; } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index c4aef3c878..ddc638b7c5 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -135,6 +135,7 @@ namespace osu.Game.Screens.Spectate case SpectatedUserState.Passed: markReceivedAllFrames(userId); + PassGameplay(userId); break; case SpectatedUserState.Failed: @@ -233,6 +234,12 @@ namespace osu.Game.Screens.Spectate /// The gameplay state. protected abstract void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState); + /// + /// Fired when a user passes gameplay. + /// + /// The user which passed. + protected virtual void PassGameplay(int userId) { } + /// /// Quits gameplay for a user. /// Thread safety is not guaranteed – should be scheduled as required. From fb2d28f7e03aba53ae905b906fcea30da9a9c4ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Apr 2024 15:30:26 +0800 Subject: [PATCH 304/581] Fix audio being paused in a spectator session when all players finish playing --- .../OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs index 9eb448d9d0..1638102089 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/SpectatorSyncManager.cs @@ -177,7 +177,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// private void updateMasterState() { - MasterClockState newState = playerClocks.Any(s => !s.IsCatchingUp) ? MasterClockState.Synchronised : MasterClockState.TooFarAhead; + // Clocks are removed as players complete the beatmap. + // Once there are no clocks we want to make sure the track plays out to the end. + MasterClockState newState = playerClocks.Count == 0 || playerClocks.Any(s => !s.IsCatchingUp) ? MasterClockState.Synchronised : MasterClockState.TooFarAhead; if (masterState == newState) return; From 694e3900dbd849bf5638efc8e8dbeec9cf57faf0 Mon Sep 17 00:00:00 2001 From: Taevas <67872932+TTTaevas@users.noreply.github.com> Date: Sat, 27 Apr 2024 23:43:27 +0200 Subject: [PATCH 305/581] Add missing space in setup wizard --- osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index b19a9c6c99..983cb0bbb4 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -128,6 +128,7 @@ namespace osu.Game.Overlays.FirstRunSetup if (available) { copyInformation.Text = FirstRunOverlayImportFromStableScreenStrings.DataMigrationNoExtraSpace; + copyInformation.AddText(@" "); // just to ensure correct spacing copyInformation.AddLink(FirstRunOverlayImportFromStableScreenStrings.LearnAboutHardLinks, LinkAction.OpenWiki, @"Client/Release_stream/Lazer/File_storage#via-hard-links"); } else if (!RuntimeInfo.IsDesktop) From 48c608e0169a1f9280f75fb3bdc0eadaa3466976 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 18 Mar 2024 12:22:23 -0700 Subject: [PATCH 306/581] Make player width a const --- osu.Game/Overlays/NowPlayingOverlay.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index ab99370603..1145ebaa2f 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -33,6 +33,7 @@ namespace osu.Game.Overlays public LocalisableString Title => NowPlayingStrings.HeaderTitle; public LocalisableString Description => NowPlayingStrings.HeaderDescription; + private const float player_width = 400; private const float player_height = 130; private const float transition_length = 800; private const float progress_height = 10; @@ -70,7 +71,7 @@ namespace osu.Game.Overlays public NowPlayingOverlay() { - Width = 400; + Width = player_width; Margin = new MarginPadding(margin); } @@ -319,15 +320,15 @@ namespace osu.Game.Overlays switch (direction) { case TrackChangeDirection.Next: - newBackground.Position = new Vector2(400, 0); + newBackground.Position = new Vector2(player_width, 0); newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(-400, 500, Easing.OutCubic); + background.MoveToX(-player_width, 500, Easing.OutCubic); break; case TrackChangeDirection.Prev: - newBackground.Position = new Vector2(-400, 0); + newBackground.Position = new Vector2(-player_width, 0); newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(400, 500, Easing.OutCubic); + background.MoveToX(player_width, 500, Easing.OutCubic); break; } From d4951a093fd9746c1ac885a8af13874dfc5390a8 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 18 Mar 2024 12:24:02 -0700 Subject: [PATCH 307/581] Scroll now playing overlay text when overflowing --- .../TestSceneNowPlayingOverlay.cs | 20 +++- osu.Game/Overlays/NowPlayingOverlay.cs | 100 +++++++++++++++++- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index d07b90025f..40e0d9250d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets.Osu; @@ -22,8 +23,6 @@ namespace osu.Game.Tests.Visual.UserInterface [BackgroundDependencyLoader] private void load() { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - nowPlayingOverlay = new NowPlayingOverlay { Origin = Anchor.Centre, @@ -37,9 +36,26 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestShowHideDisable() { + AddStep(@"set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo)); AddStep(@"show", () => nowPlayingOverlay.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); AddStep(@"hide", () => nowPlayingOverlay.Hide()); } + + [Test] + public void TestLongMetadata() + { + AddStep(@"set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + Metadata = + { + Artist = "very very very very very very very very very very very long artist", + ArtistUnicode = "very very very very very very very very very very very long artist", + Title = "very very very very very very very very very very very long title", + TitleUnicode = "very very very very very very very very very very very long title", + } + })); + AddStep(@"show", () => nowPlayingOverlay.Show()); + } } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 1145ebaa2f..be405257ca 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -6,6 +6,7 @@ using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -48,7 +49,7 @@ namespace osu.Game.Overlays private IconButton nextButton = null!; private IconButton playlistButton = null!; - private SpriteText title = null!, artist = null!; + private ScrollingTextContainer title = null!, artist = null!; private PlaylistOverlay? playlist; @@ -102,7 +103,7 @@ namespace osu.Game.Overlays Children = new[] { background = Empty(), - title = new OsuSpriteText + title = new ScrollingTextContainer { Origin = Anchor.BottomCentre, Anchor = Anchor.TopCentre, @@ -111,7 +112,7 @@ namespace osu.Game.Overlays Colour = Color4.White, Text = @"Nothing to play", }, - artist = new OsuSpriteText + artist = new ScrollingTextContainer { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, @@ -470,5 +471,98 @@ namespace osu.Game.Overlays base.OnHoverLost(e); } } + + private partial class ScrollingTextContainer : CompositeDrawable + { + private const float initial_move_delay = 1000; + private const float pixels_per_second = 50; + + private LocalisableString text; + private OsuSpriteText mainSpriteText = null!; + private OsuSpriteText fillerSpriteText = null!; + + public LocalisableString Text + { + get => text; + set + { + text = value; + Schedule(updateText); + } + } + + public FontUsage Font + { + set => + Schedule(() => + { + mainSpriteText.Font = value; + fillerSpriteText.Font = value; + + updateText(); + }); + } + + public ScrollingTextContainer() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + mainSpriteText = new OsuSpriteText { Padding = new MarginPadding { Horizontal = margin } }, + fillerSpriteText = new OsuSpriteText { Padding = new MarginPadding { Horizontal = margin }, Alpha = 0 }, + } + }; + } + + private void updateText() + { + mainSpriteText.Text = text; + fillerSpriteText.Alpha = 0; + + ClearTransforms(); + X = 0; + + float textOverflowWidth = mainSpriteText.Width - player_width; + + if (textOverflowWidth > 0) + { + fillerSpriteText.Alpha = 1; + fillerSpriteText.Text = text; + + float initialX; + float targetX; + + if (Anchor.HasFlagFast(Anchor.x0)) + { + initialX = 0; + targetX = -mainSpriteText.Width; + } + else if (Anchor.HasFlagFast(Anchor.x1)) + { + initialX = (textOverflowWidth + mainSpriteText.Width) / 2; + targetX = (textOverflowWidth - mainSpriteText.Width) / 2; + } + else // Anchor.x2 + { + initialX = textOverflowWidth + mainSpriteText.Width; + targetX = textOverflowWidth; + } + + this.MoveToX(initialX) + .Delay(initial_move_delay) + .MoveToX(targetX, mainSpriteText.Width * 1000 / pixels_per_second) + .Loop(); + } + } + } } } From b262497083ccef7d72b9c838d37bf13706ee4faf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 28 Apr 2024 19:07:39 +0800 Subject: [PATCH 308/581] Check realm file can be written to before attempting further initialisation Rather than creating a "corrupt" realm file in such cases, the game will now refuse to start. This behaviour is usually what we want. In most cases a second click on the game will start it successfully (the previous instance's file handles are still doing stuff, or windows defender is being silly). Closes https://github.com/ppy/osu/issues/28018. --- osu.Game/Database/RealmAccess.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 167d170c81..4bc7ec4979 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -300,6 +300,21 @@ namespace osu.Game.Database private Realm prepareFirstRealmAccess() { + // Before attempting to initialise realm, make sure the realm file isn't locked and has correct permissions. + // + // This is to avoid failures like: + // Realms.Exceptions.RealmException: SetEndOfFile() failed: unknown error (1224) + // + // which can occur due to file handles still being open by a previous instance. + if (storage.Exists(Filename)) + { + // If this fails we allow it to block game startup. + // It's better than any alternative we can offer. + using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) + { + } + } + string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}"; // Attempt to recover a newer database version if available. @@ -321,7 +336,7 @@ namespace osu.Game.Database { Logger.Error(e, "Your local database is too new to work with this version of osu!. Please close osu! and install the latest release to recover your data."); - // If a newer version database already exists, don't backup again. We can presume that the first backup is the one we care about. + // If a newer version database already exists, don't create another backup. We can presume that the first backup is the one we care about. if (!storage.Exists(newerVersionFilename)) createBackup(newerVersionFilename); } From a4bc5a8fc9059e2f13d736965e8cc52eff95f7ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 10:35:37 +0800 Subject: [PATCH 309/581] Use helper method for backup retry attempts --- osu.Game/Database/RealmAccess.cs | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 4bc7ec4979..b5faa898e7 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -35,6 +35,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Skinning; +using osu.Game.Utils; using osuTK.Input; using Realms; using Realms.Exceptions; @@ -1157,33 +1158,18 @@ namespace osu.Game.Database { Logger.Log($"Creating full realm database backup at {backupFilename}", LoggingTarget.Database); - int attempts = 10; - - while (true) + FileUtils.AttemptOperation(() => { - try + using (var source = storage.GetStream(Filename, mode: FileMode.Open)) { - using (var source = storage.GetStream(Filename, mode: FileMode.Open)) - { - // source may not exist. - if (source == null) - return; + // source may not exist. + if (source == null) + return; - using (var destination = storage.GetStream(backupFilename, FileAccess.Write, FileMode.CreateNew)) - source.CopyTo(destination); - } - - return; + using (var destination = storage.GetStream(backupFilename, FileAccess.Write, FileMode.CreateNew)) + source.CopyTo(destination); } - catch (IOException) - { - if (attempts-- <= 0) - throw; - - // file may be locked during use. - Thread.Sleep(500); - } - } + }, 20); } /// From 1c1ee22aa70cfa3e92786fba7a208f2af08a2e69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 10:36:49 +0800 Subject: [PATCH 310/581] Add retry attempts to hopefully fix windows tests runs --- osu.Game/Database/RealmAccess.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index b5faa898e7..31ae22178f 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -311,9 +311,12 @@ namespace osu.Game.Database { // If this fails we allow it to block game startup. // It's better than any alternative we can offer. - using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) + FileUtils.AttemptOperation(() => { - } + using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) + { + } + }); } string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}"; From a3d239c11aa85215b3565171de2b580b8b1c8411 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 18:48:07 +0800 Subject: [PATCH 311/581] Remove unused method --- osu.Game/Database/RealmArchiveModelImporter.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index bc4954c6ea..0014e246dc 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -449,16 +449,6 @@ namespace osu.Game.Database return reader.Name.ComputeSHA2Hash(); } - /// - /// Create all required s for the provided archive, adding them to the global file store. - /// - private List createFileInfos(ArchiveReader reader, RealmFileStore files, Realm realm) - { - var fileInfos = new List(); - - return fileInfos; - } - private IEnumerable<(string original, string shortened)> getShortenedFilenames(ArchiveReader reader) { string prefix = reader.Filenames.GetCommonPrefix(); From 45c2327509911032984c863acb912406c44075fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 Apr 2024 13:00:22 +0200 Subject: [PATCH 312/581] Apply adjustments after framework-side `FriendlyGameName` changes --- osu.Desktop/Program.cs | 6 +++++- osu.Game/OsuGameBase.cs | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 29b05a402f..d8364fc6e6 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -107,7 +107,11 @@ namespace osu.Desktop } } - using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions + { + IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null, + FriendlyGameName = OsuGameBase.GAME_NAME, + })) { if (!host.IsPrimaryInstance) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0122afb239..5533ee8337 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -75,6 +75,12 @@ namespace osu.Game { public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" }; +#if DEBUG + public const string GAME_NAME = "osu! (development)"; +#else + public const string GAME_NAME = "osu!"; +#endif + public const string OSU_PROTOCOL = "osu://"; public const string CLIENT_STREAM_NAME = @"lazer"; @@ -241,11 +247,7 @@ namespace osu.Game public OsuGameBase() { - Name = @"osu!"; - -#if DEBUG - Name += " (development)"; -#endif + Name = GAME_NAME; allowableExceptions = UnhandledExceptionsBeforeCrash; } From 9fc56f1cc7e916a7c41f7073be25b47642860df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 Apr 2024 13:07:36 +0200 Subject: [PATCH 313/581] Apply adjustments after migration of android to SDL3 --- osu.Android/AndroidJoystickSettings.cs | 76 -------------------- osu.Android/AndroidMouseSettings.cs | 97 -------------------------- osu.Android/OsuGameAndroid.cs | 22 ------ 3 files changed, 195 deletions(-) delete mode 100644 osu.Android/AndroidJoystickSettings.cs delete mode 100644 osu.Android/AndroidMouseSettings.cs diff --git a/osu.Android/AndroidJoystickSettings.cs b/osu.Android/AndroidJoystickSettings.cs deleted file mode 100644 index bf69461f0d..0000000000 --- a/osu.Android/AndroidJoystickSettings.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Android.Input; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Localisation; -using osu.Game.Localisation; -using osu.Game.Overlays.Settings; - -namespace osu.Android -{ - public partial class AndroidJoystickSettings : SettingsSubsection - { - protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad; - - private readonly AndroidJoystickHandler joystickHandler; - - private readonly Bindable enabled = new BindableBool(true); - - private SettingsSlider deadzoneSlider = null!; - - private Bindable handlerDeadzone = null!; - - private Bindable localDeadzone = null!; - - public AndroidJoystickSettings(AndroidJoystickHandler joystickHandler) - { - this.joystickHandler = joystickHandler; - } - - [BackgroundDependencyLoader] - private void load() - { - // use local bindable to avoid changing enabled state of game host's bindable. - handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy(); - localDeadzone = handlerDeadzone.GetUnboundCopy(); - - Children = new Drawable[] - { - new SettingsCheckbox - { - LabelText = CommonStrings.Enabled, - Current = enabled - }, - deadzoneSlider = new SettingsSlider - { - LabelText = JoystickSettingsStrings.DeadzoneThreshold, - KeyboardStep = 0.01f, - DisplayAsPercentage = true, - Current = localDeadzone, - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - enabled.BindTo(joystickHandler.Enabled); - enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true); - - handlerDeadzone.BindValueChanged(val => - { - bool disabled = localDeadzone.Disabled; - - localDeadzone.Disabled = false; - localDeadzone.Value = val.NewValue; - localDeadzone.Disabled = disabled; - }, true); - - localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue); - } - } -} diff --git a/osu.Android/AndroidMouseSettings.cs b/osu.Android/AndroidMouseSettings.cs deleted file mode 100644 index fd01b11164..0000000000 --- a/osu.Android/AndroidMouseSettings.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Android.OS; -using osu.Framework.Allocation; -using osu.Framework.Android.Input; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Localisation; -using osu.Game.Configuration; -using osu.Game.Localisation; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections.Input; - -namespace osu.Android -{ - public partial class AndroidMouseSettings : SettingsSubsection - { - private readonly AndroidMouseHandler mouseHandler; - - protected override LocalisableString Header => MouseSettingsStrings.Mouse; - - private Bindable handlerSensitivity = null!; - - private Bindable localSensitivity = null!; - - private Bindable relativeMode = null!; - - public AndroidMouseSettings(AndroidMouseHandler mouseHandler) - { - this.mouseHandler = mouseHandler; - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager osuConfig) - { - // use local bindable to avoid changing enabled state of game host's bindable. - handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy(); - localSensitivity = handlerSensitivity.GetUnboundCopy(); - - relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy(); - - // High precision/pointer capture is only available on Android 8.0 and up - if (Build.VERSION.SdkInt >= BuildVersionCodes.O) - { - AddRange(new Drawable[] - { - new SettingsCheckbox - { - LabelText = MouseSettingsStrings.HighPrecisionMouse, - TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip, - Current = relativeMode, - Keywords = new[] { @"raw", @"input", @"relative", @"cursor", @"captured", @"pointer" }, - }, - new MouseSettings.SensitivitySetting - { - LabelText = MouseSettingsStrings.CursorSensitivity, - Current = localSensitivity, - }, - }); - } - - AddRange(new Drawable[] - { - new SettingsCheckbox - { - LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust, - TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip, - Current = osuConfig.GetBindable(OsuSetting.MouseDisableWheel), - }, - new SettingsCheckbox - { - LabelText = MouseSettingsStrings.DisableClicksDuringGameplay, - Current = osuConfig.GetBindable(OsuSetting.MouseDisableButtons), - }, - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true); - - handlerSensitivity.BindValueChanged(val => - { - bool disabled = localSensitivity.Disabled; - - localSensitivity.Disabled = false; - localSensitivity.Value = val.NewValue; - localSensitivity.Disabled = disabled; - }, true); - - localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue); - } - } -} diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 52cfb67f42..a235913ef3 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -5,13 +5,9 @@ using System; using Android.App; using Microsoft.Maui.Devices; using osu.Framework.Allocation; -using osu.Framework.Android.Input; using osu.Framework.Extensions.ObjectExtensions; -using osu.Framework.Input.Handlers; using osu.Framework.Platform; using osu.Game; -using osu.Game.Overlays.Settings; -using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Updater; using osu.Game.Utils; @@ -88,24 +84,6 @@ namespace osu.Android protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo(); - public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler) - { - switch (handler) - { - case AndroidMouseHandler mh: - return new AndroidMouseSettings(mh); - - case AndroidJoystickHandler jh: - return new AndroidJoystickSettings(jh); - - case AndroidTouchHandler th: - return new TouchSettings(th); - - default: - return base.CreateSettingsSubsectionFor(handler); - } - } - private class AndroidBatteryInfo : BatteryInfo { public override double? ChargeLevel => Battery.ChargeLevel; From fa3aeca09d8656ec76ae9e0401047b4f5f15646a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 Apr 2024 14:06:02 +0200 Subject: [PATCH 314/581] Add failing test for skins not saving on change --- .../TestSceneSkinEditorNavigation.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 9c180d43da..38fb2846aa 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -321,6 +322,30 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("nested input disabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType().All(manager => !manager.UseParentInput)); } + [Test] + public void TestSkinSavesOnChange() + { + advanceToSongSelect(); + openSkinEditor(); + + Guid editedSkinId = Guid.Empty; + AddStep("save skin id", () => editedSkinId = Game.Dependencies.Get().CurrentSkinInfo.Value.ID); + AddStep("add skinnable component", () => + { + skinEditor.ChildrenOfType().First().TriggerClick(); + }); + + AddStep("change to triangles skin", () => Game.Dependencies.Get().SetSkinFromConfiguration(SkinInfo.TRIANGLES_SKIN.ToString())); + AddUntilStep("components loaded", () => Game.ChildrenOfType().All(c => c.ComponentsLoaded)); + // sort of implicitly relies on song select not being skinnable. + // TODO: revisit if the above ever changes + AddUntilStep("skin changed", () => !skinEditor.ChildrenOfType().Any()); + + AddStep("change back to modified skin", () => Game.Dependencies.Get().SetSkinFromConfiguration(editedSkinId.ToString())); + AddUntilStep("components loaded", () => Game.ChildrenOfType().All(c => c.ComponentsLoaded)); + AddUntilStep("changes saved", () => skinEditor.ChildrenOfType().Any()); + } + private void advanceToSongSelect() { PushAndConfirm(() => songSelect = new TestPlaySongSelect()); From f78abf801c2f2d9af66bc6baa71c0a0c6ec52406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 Apr 2024 14:06:23 +0200 Subject: [PATCH 315/581] Autosave edited skin on change --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index bc929177d1..690c6b35e3 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -255,8 +255,11 @@ namespace osu.Game.Overlays.SkinEditor // schedule ensures this only happens when the skin editor is visible. // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). // probably something which will be factored out in a future database refactor so not too concerning for now. - currentSkin.BindValueChanged(_ => + currentSkin.BindValueChanged(val => { + if (val.OldValue != null && hasBegunMutating) + save(val.OldValue); + hasBegunMutating = false; Scheduler.AddOnce(skinChanged); }, true); @@ -537,7 +540,9 @@ namespace osu.Game.Overlays.SkinEditor protected void Redo() => changeHandler?.RestoreState(1); - public void Save(bool userTriggered = true) + public void Save(bool userTriggered = true) => save(currentSkin.Value); + + private void save(Skin skin, bool userTriggered = true) { if (!hasBegunMutating) return; @@ -551,11 +556,11 @@ namespace osu.Game.Overlays.SkinEditor return; foreach (var t in targetContainers) - currentSkin.Value.UpdateDrawableTarget(t); + skin.UpdateDrawableTarget(t); // In the case the save was user triggered, always show the save message to make them feel confident. - if (skins.Save(skins.CurrentSkin.Value) || userTriggered) - onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, currentSkin.Value.SkinInfo.ToString() ?? "Unknown")); + if (skins.Save(skin) || userTriggered) + onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, skin.SkinInfo.ToString() ?? "Unknown")); } protected override bool OnHover(HoverEvent e) => true; From 85c085e5879d59fea0533c78261bf8b111d878c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 23:22:25 +0800 Subject: [PATCH 316/581] Reduce startup volume Constant complaints about startup volume mean we should reduce it further and let users adjust as they see fit. --- osu.Game/OsuGame.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 98533a5c82..7c89314014 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -841,7 +841,10 @@ namespace osu.Game { // General expectation that osu! starts in fullscreen by default (also gives the most predictable performance). // However, macOS is bound to have issues when using exclusive fullscreen as it takes full control away from OS, therefore borderless is default there. - { FrameworkSetting.WindowMode, RuntimeInfo.OS == RuntimeInfo.Platform.macOS ? WindowMode.Borderless : WindowMode.Fullscreen } + { FrameworkSetting.WindowMode, RuntimeInfo.OS == RuntimeInfo.Platform.macOS ? WindowMode.Borderless : WindowMode.Fullscreen }, + { FrameworkSetting.VolumeUniversal, 0.6 }, + { FrameworkSetting.VolumeMusic, 0.6 }, + { FrameworkSetting.VolumeEffect, 0.6 }, }; } From fd3f4a9e7b30560c6270845af4a77ae1ed8073ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 22:26:44 +0800 Subject: [PATCH 317/581] Preserve storyboard events when saving a beatmap in the editor Until we have full encoding support for storyboards, this stop-gap measure ensures that storyboards don't just disappear from existence. --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 15 +++++++++++++++ osu.Game/Beatmaps/Beatmap.cs | 2 ++ osu.Game/Beatmaps/BeatmapConverter.cs | 1 + osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 14 ++++++++++++++ osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 3 +++ osu.Game/Beatmaps/IBeatmap.cs | 6 ++++++ .../Rulesets/Difficulty/DifficultyCalculator.cs | 2 ++ osu.Game/Screens/Edit/EditorBeatmap.cs | 2 ++ osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + 9 files changed, 46 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index e847b61fbe..ef30f020ce 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -37,6 +37,21 @@ namespace osu.Game.Tests.Beatmaps.Formats private static IEnumerable allBeatmaps = beatmaps_resource_store.GetAvailableResources().Where(res => res.EndsWith(".osu", StringComparison.Ordinal)); + [Test] + public void TestUnsupportedStoryboardEvents() + { + const string name = "Resources/storyboard_only_video.osu"; + + var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); + var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name); + + Assert.That(decoded.beatmap.UnhandledEventLines.Count, Is.EqualTo(1)); + Assert.That(decoded.beatmap.UnhandledEventLines.Single(), Is.EqualTo("Video,0,\"video.avi\"")); + + Assert.That(decodedAfterEncode.beatmap.UnhandledEventLines.Count, Is.EqualTo(1)); + Assert.That(decodedAfterEncode.beatmap.UnhandledEventLines.Single(), Is.EqualTo("Video,0,\"video.avi\"")); + } + [TestCaseSource(nameof(allBeatmaps))] public void TestEncodeDecodeStability(string name) { diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 6db9febf36..ae77e4adcf 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -63,6 +63,8 @@ namespace osu.Game.Beatmaps public List Breaks { get; set; } = new List(); + public List UnhandledEventLines { get; set; } = new List(); + [JsonIgnore] public double TotalBreakTime => Breaks.Sum(b => b.Duration); diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index c7c244bf0e..b68c80d4b3 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -66,6 +66,7 @@ namespace osu.Game.Beatmaps beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.HitObjects = convertHitObjects(original.HitObjects, original, cancellationToken).OrderBy(s => s.StartTime).ToList(); beatmap.Breaks = original.Breaks; + beatmap.UnhandledEventLines = original.UnhandledEventLines; return beatmap; } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 386dada328..7407c3590f 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -420,6 +420,10 @@ namespace osu.Game.Beatmaps.Formats if (!Enum.TryParse(split[0], out LegacyEventType type)) throw new InvalidDataException($@"Unknown event type: {split[0]}"); + // Until we have full storyboard encoder coverage, let's track any lines which aren't handled + // and store them to a temporary location such that they aren't lost on editor save / export. + bool lineSupportedByEncoder = false; + switch (type) { case LegacyEventType.Sprite: @@ -427,7 +431,11 @@ namespace osu.Game.Beatmaps.Formats // In some older beatmaps, it is not present and replaced by a storyboard-level background instead. // Allow the first sprite (by file order) to act as the background in such cases. if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) + { beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); + lineSupportedByEncoder = true; + } + break; case LegacyEventType.Video: @@ -439,12 +447,14 @@ namespace osu.Game.Beatmaps.Formats if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant())) { beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + lineSupportedByEncoder = true; } break; case LegacyEventType.Background: beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); + lineSupportedByEncoder = true; break; case LegacyEventType.Break: @@ -452,8 +462,12 @@ namespace osu.Game.Beatmaps.Formats double end = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2]))); beatmap.Breaks.Add(new BreakPeriod(start, end)); + lineSupportedByEncoder = true; break; } + + if (!lineSupportedByEncoder) + beatmap.UnhandledEventLines.Add(line); } private void handleTimingPoint(string line) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 290d29090a..186b565c39 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -156,6 +156,9 @@ namespace osu.Game.Beatmaps.Formats foreach (var b in beatmap.Breaks) writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}")); + + foreach (string l in beatmap.UnhandledEventLines) + writer.WriteLine(l); } private void handleControlPoints(TextWriter writer) diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 6fe494ca0f..5cc38e5b84 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -42,6 +42,12 @@ namespace osu.Game.Beatmaps /// List Breaks { get; } + /// + /// All lines from the [Events] section which aren't handled in the encoding process yet. + /// These lines shoule be written out to the beatmap file on save or export. + /// + List UnhandledEventLines { get; } + /// /// Total amount of break time in the beatmap. /// diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 1599dff8d9..d37cfc28b9 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -330,6 +330,8 @@ namespace osu.Game.Rulesets.Difficulty } public List Breaks => baseBeatmap.Breaks; + public List UnhandledEventLines => baseBeatmap.UnhandledEventLines; + public double TotalBreakTime => baseBeatmap.TotalBreakTime; public IEnumerable GetStatistics() => baseBeatmap.GetStatistics(); public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength(); diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index dc1fda13f4..7a3ea474fb 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -174,6 +174,8 @@ namespace osu.Game.Screens.Edit public List Breaks => PlayableBeatmap.Breaks; + public List UnhandledEventLines => PlayableBeatmap.UnhandledEventLines; + public double TotalBreakTime => PlayableBeatmap.TotalBreakTime; public IReadOnlyList HitObjects => PlayableBeatmap.HitObjects; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index ff670e1232..de7bcfcfaa 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -27,6 +27,7 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo = baseBeatmap.BeatmapInfo; ControlPointInfo = baseBeatmap.ControlPointInfo; Breaks = baseBeatmap.Breaks; + UnhandledEventLines = baseBeatmap.UnhandledEventLines; if (withHitObjects) HitObjects = baseBeatmap.HitObjects; From 19897c4c074fbca635b9bdbee66f4fb6e368a126 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 00:50:40 +0800 Subject: [PATCH 318/581] Add testing for actual presence of video after encode-decode --- .../Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index ef30f020ce..b931896898 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Taiko; using osu.Game.Skinning; +using osu.Game.Storyboards; using osu.Game.Tests.Resources; using osuTK; @@ -43,13 +44,14 @@ namespace osu.Game.Tests.Beatmaps.Formats const string name = "Resources/storyboard_only_video.osu"; var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name); - var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name); - Assert.That(decoded.beatmap.UnhandledEventLines.Count, Is.EqualTo(1)); Assert.That(decoded.beatmap.UnhandledEventLines.Single(), Is.EqualTo("Video,0,\"video.avi\"")); - Assert.That(decodedAfterEncode.beatmap.UnhandledEventLines.Count, Is.EqualTo(1)); - Assert.That(decodedAfterEncode.beatmap.UnhandledEventLines.Single(), Is.EqualTo("Video,0,\"video.avi\"")); + var memoryStream = encodeToLegacy(decoded); + + var storyboard = new LegacyStoryboardDecoder().Decode(new LineBufferedReader(memoryStream)); + StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video"); + Assert.That(video.Elements.Count, Is.EqualTo(1)); } [TestCaseSource(nameof(allBeatmaps))] From b455e793dd7f3e3d8a75c57d3304e326ad75ab32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 01:14:58 +0800 Subject: [PATCH 319/581] Add test coverage of reading japanese file from stable export --- .../Resources/Archives/japanese-filename.osz | Bin 0 -> 816 bytes .../Skins/TestSceneBeatmapSkinResources.cs | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 osu.Game.Tests/Resources/Archives/japanese-filename.osz diff --git a/osu.Game.Tests/Resources/Archives/japanese-filename.osz b/osu.Game.Tests/Resources/Archives/japanese-filename.osz new file mode 100644 index 0000000000000000000000000000000000000000..4825c88179bb6d351f8766c4b4a17d89a50a0af5 GIT binary patch literal 816 zcmWIWW@Zs#U|`^2h!mU`q3R&=>KG#fg9S4KgE~+&v8W`oxI{r$p(L{;CsjeCAhn>N zQd1#1B{MB8Gr2UUq%u}7zqqtE$Twfgk!SDvE&mhyz6c0KtqLn{p1`k?5n%4SG|X~_ zq;qWB%&F-L3-|Av$Ji9IP_8Vz?)6l9$IN{%u3jy@@gwDM_XQ!-4SBL=Gq(H^vf-L= zV2#=f&b>E1+utqUHH)o#=}nekr+MwyH_96w(h~b(9of?+SUZ{N{WHcJVor1JMF{!y z^l7MlV4E$*^K)rOxocg=1h*|qB_gzEzU@0{_Qm~*-SyCKZ%#b!7r1tG&6n9pOx&FF zjOWU{nsUGW!EE)(YvndDstaBXusi-?MzPEKRKCI$(K)wcV{H}Px#FUubWY^N2(96i zHe2D!GA;1E@A`$Z{a>y|D@afIk(6KbB}~WceY!H+r+CGM|1_$OxQWe6pYkGc_WKup ztdcTYblDB|_o?b4qr)&hc$rPbw$9 zS+L$&g2#wlFe7?$5&$@yl&)v2%MRPC{zeJOZuv_kp3n)O!39UpCeI%+XL{y*z` zsQUWj2j9i-(+qxFe|N_7X(!)n+McOjta beatmap = importBeatmapFromArchives(@"japanese-filename.osz")); + AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); + } + [Test] public void TestRetrieveOggAudio() { From 97da47f69ca90fe35dad7914b04e97c7363e7df4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 01:27:45 +0800 Subject: [PATCH 320/581] Add test coverage of reading japanese filename after exporting --- .../Skins/TestSceneBeatmapSkinResources.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index b566e892cd..e0922c52f7 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.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 System.IO; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio.Track; @@ -12,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual; +using MemoryStream = System.IO.MemoryStream; namespace osu.Game.Tests.Skins { @@ -25,9 +27,23 @@ namespace osu.Game.Tests.Skins public void TestRetrieveJapaneseFilename() { IWorkingBeatmap beatmap = null!; + MemoryStream outStream = null!; + // Ensure importer encoding is correct AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"japanese-filename.osz")); AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); + + // Ensure exporter encoding is correct (round trip) + AddStep("export", () => + { + outStream = new MemoryStream(); + + new LegacyBeatmapExporter(LocalStorage) + .ExportToStream((BeatmapSetInfo)beatmap.BeatmapInfo.BeatmapSet!, outStream, null); + }); + + AddStep("import beatmap again", () => beatmap = importBeatmapFromStream(outStream)); + AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); } [Test] @@ -54,6 +70,12 @@ namespace osu.Game.Tests.Skins AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"spinner-osu")) != null); } + private IWorkingBeatmap importBeatmapFromStream(Stream stream) + { + var imported = beatmaps.Import(new ImportTask(stream, "filename.osz")).GetResultSafely(); + return imported.AsNonNull().PerformRead(s => beatmaps.GetWorkingBeatmap(s.Beatmaps[0])); + } + private IWorkingBeatmap importBeatmapFromArchives(string filename) { var imported = beatmaps.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely(); From c8f7f2215b4c9b3bcb5df8fdc46b7f906890dd2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Apr 2024 18:49:17 +0800 Subject: [PATCH 321/581] Force encoding to Shift-JIS for archive filenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After way too much time investigating this, the encoding situation is not great right now. - Stable sets the "default code page" to be used for encoding filenames to Shift-JIS (932): https://github.com/peppy/osu-stable-reference/blob/c29ebd7fc52113013fb4ac2db230699d81e1fe2c/osu!/GameBase.cs#L3099 - Lazer does nothing (therefore using UTF-8). When importing to lazer, stable files are assumed to be UTF-8. This means that the linked beatmaps don't work correctly. Forcing lazer to decompress *and* compress using Shift-JIS will fix this. Here's a rough idea of how things look for japanese character filenames in current `master`: | | stable | lazer | |--------|--------|--------| | export encoding | shift-jis | utf8 | | utf8 [bit flag](https://superuser.com/a/1507988) set | ❌ | ❌ | | import stable export osz | ✅ | ❌ | | import lazer export osz | ❌ | ✅ | | windows unzip | ❌ | ❌ | | macos unzip | ✅ | ✅ | and after this change | | stable | lazer | |--------|--------|--------| | export encoding | shift-jis | shift-jis | | utf8 [bit flag](https://superuser.com/a/1507988) set | ❌ | ❌ | | import stable export osz | ✅ | ✅ | | import lazer export osz | ✅ | ✅ | | windows unzip | ❌ | ❌ | | macos unzip | ✅ | ✅ | A future endeavour to improve compatibility would be to look at setting the utf8 flag in lazer, switching the default to utf8, and ensuring the stable supports this flag (I don't believe it does right now). --- osu.Game/Database/LegacyArchiveExporter.cs | 6 +++++- osu.Game/IO/Archives/ZipArchiveReader.cs | 24 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs index 9805207591..1d9d252220 100644 --- a/osu.Game/Database/LegacyArchiveExporter.cs +++ b/osu.Game/Database/LegacyArchiveExporter.cs @@ -7,6 +7,7 @@ using System.Threading; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Extensions; +using osu.Game.IO.Archives; using osu.Game.Overlays.Notifications; using Realms; using SharpCompress.Common; @@ -29,7 +30,10 @@ namespace osu.Game.Database public override void ExportToStream(TModel model, Stream outputStream, ProgressNotification? notification, CancellationToken cancellationToken = default) { - using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate))) + using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate) + { + ArchiveEncoding = ZipArchiveReader.DEFAULT_ENCODING + })) { int i = 0; int fileCount = model.Files.Count(); diff --git a/osu.Game/IO/Archives/ZipArchiveReader.cs b/osu.Game/IO/Archives/ZipArchiveReader.cs index cc5c65d184..6bb2a314e7 100644 --- a/osu.Game/IO/Archives/ZipArchiveReader.cs +++ b/osu.Game/IO/Archives/ZipArchiveReader.cs @@ -7,23 +7,45 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using Microsoft.Toolkit.HighPerformance; using osu.Framework.IO.Stores; using SharpCompress.Archives.Zip; +using SharpCompress.Common; +using SharpCompress.Readers; using SixLabors.ImageSharp.Memory; namespace osu.Game.IO.Archives { public sealed class ZipArchiveReader : ArchiveReader { + /// + /// Archives created by osu!stable still write out as Shift-JIS. + /// We want to force this fallback rather than leave it up to the library/system. + /// In the future we may want to change exports to set the zip UTF-8 flag and use that instead. + /// + public static readonly ArchiveEncoding DEFAULT_ENCODING; + private readonly Stream archiveStream; private readonly ZipArchive archive; + static ZipArchiveReader() + { + // Required to support rare code pages. + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + DEFAULT_ENCODING = new ArchiveEncoding(Encoding.GetEncoding(932), Encoding.GetEncoding(932)); + } + public ZipArchiveReader(Stream archiveStream, string name = null) : base(name) { this.archiveStream = archiveStream; - archive = ZipArchive.Open(archiveStream); + + archive = ZipArchive.Open(archiveStream, new ReaderOptions + { + ArchiveEncoding = DEFAULT_ENCODING + }); } public override Stream GetStream(string name) From a8416f3572bfa143c6019322365211fb0df1837b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 12:39:18 +0800 Subject: [PATCH 322/581] Move `exists` check inside retry operation Might help? --- osu.Game/Database/RealmAccess.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 31ae22178f..465d7b15a7 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -307,17 +307,17 @@ namespace osu.Game.Database // Realms.Exceptions.RealmException: SetEndOfFile() failed: unknown error (1224) // // which can occur due to file handles still being open by a previous instance. - if (storage.Exists(Filename)) + // + // If this fails we allow it to block game startup. It's better than any alternative we can offer. + FileUtils.AttemptOperation(() => { - // If this fails we allow it to block game startup. - // It's better than any alternative we can offer. - FileUtils.AttemptOperation(() => + if (storage.Exists(Filename)) { using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) { } - }); - } + } + }); string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}"; From 0bfad74907be51a9755e43cad6f44db372b57f21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 14:09:29 +0800 Subject: [PATCH 323/581] Move realm error handling to avoid triggering in test scenarios --- osu.Game/Database/RealmAccess.cs | 38 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 465d7b15a7..057bbe02e6 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -301,24 +301,6 @@ namespace osu.Game.Database private Realm prepareFirstRealmAccess() { - // Before attempting to initialise realm, make sure the realm file isn't locked and has correct permissions. - // - // This is to avoid failures like: - // Realms.Exceptions.RealmException: SetEndOfFile() failed: unknown error (1224) - // - // which can occur due to file handles still being open by a previous instance. - // - // If this fails we allow it to block game startup. It's better than any alternative we can offer. - FileUtils.AttemptOperation(() => - { - if (storage.Exists(Filename)) - { - using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) - { - } - } - }); - string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}"; // Attempt to recover a newer database version if available. @@ -346,6 +328,26 @@ namespace osu.Game.Database } else { + // This error can occur due to file handles still being open by a previous instance. + // If this is the case, rather than assuming the realm file is corrupt, block game startup. + if (e.Message.StartsWith("SetEndOfFile() failed", StringComparison.Ordinal)) + { + // This will throw if the realm file is not available for write access after 5 seconds. + FileUtils.AttemptOperation(() => + { + if (storage.Exists(Filename)) + { + using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite)) + { + } + } + }, 20); + + // If the above eventually succeeds, try and continue startup as per normal. + // This may throw again but let's allow it to, and block startup. + return getRealmInstance(); + } + Logger.Error(e, "Realm startup failed with unrecoverable error; starting with a fresh database. A backup of your database has been made."); createBackup($"{Filename.Replace(realm_extension, string.Empty)}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}_corrupt{realm_extension}"); } From ba9f4e4baff591cc4b35787216edfa4d88ccabc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 16:08:30 +0800 Subject: [PATCH 324/581] Don't skip lines in beatmap decoding Was added in cc76c58f5f250151bb85ad5efa3f6ce008f0cbb0 without any specific reasoning. Likely not required (and will fix some storyboard elements inside `.osu` files from not being correctly saved). --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 7407c3590f..84f3c0d82a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -167,8 +167,6 @@ namespace osu.Game.Beatmaps.Formats beatmapInfo.SamplesMatchPlaybackRate = false; } - protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(' ') || line.StartsWith('_'); - protected override void ParseLine(Beatmap beatmap, Section section, string line) { switch (section) From a3213fc36dd23f835db6cac300b1963948757e75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 21:40:04 +0800 Subject: [PATCH 325/581] Change `.olz` to use UTF-8 encoding --- osu.Game/Database/BeatmapExporter.cs | 2 ++ osu.Game/Database/LegacyArchiveExporter.cs | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/BeatmapExporter.cs b/osu.Game/Database/BeatmapExporter.cs index f37c57dea5..01ef09d3d7 100644 --- a/osu.Game/Database/BeatmapExporter.cs +++ b/osu.Game/Database/BeatmapExporter.cs @@ -17,6 +17,8 @@ namespace osu.Game.Database { } + protected override bool UseFixedEncoding => false; + protected override string FileExtension => @".olz"; } } diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs index 1d9d252220..b0e5304ffd 100644 --- a/osu.Game/Database/LegacyArchiveExporter.cs +++ b/osu.Game/Database/LegacyArchiveExporter.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; +using System.Text; using System.Threading; using osu.Framework.Logging; using osu.Framework.Platform; @@ -23,6 +24,11 @@ namespace osu.Game.Database public abstract class LegacyArchiveExporter : LegacyExporter where TModel : RealmObject, IHasNamedFiles, IHasGuidPrimaryKey { + /// + /// Whether to always use Shift-JIS encoding for archive filenames (like osu!stable did). + /// + protected virtual bool UseFixedEncoding => true; + protected LegacyArchiveExporter(Storage storage) : base(storage) { @@ -32,7 +38,7 @@ namespace osu.Game.Database { using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate) { - ArchiveEncoding = ZipArchiveReader.DEFAULT_ENCODING + ArchiveEncoding = UseFixedEncoding ? ZipArchiveReader.DEFAULT_ENCODING : new ArchiveEncoding(Encoding.UTF8, Encoding.UTF8) })) { int i = 0; From 6a7e2dc258a5cb4d2ea2a72b022cf75eec32186e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Apr 2024 21:47:03 +0800 Subject: [PATCH 326/581] Fix formatting --- osu.Game/Database/LegacyArchiveExporter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/LegacyArchiveExporter.cs b/osu.Game/Database/LegacyArchiveExporter.cs index b0e5304ffd..e4d3ed4681 100644 --- a/osu.Game/Database/LegacyArchiveExporter.cs +++ b/osu.Game/Database/LegacyArchiveExporter.cs @@ -36,10 +36,12 @@ namespace osu.Game.Database public override void ExportToStream(TModel model, Stream outputStream, ProgressNotification? notification, CancellationToken cancellationToken = default) { - using (var writer = new ZipWriter(outputStream, new ZipWriterOptions(CompressionType.Deflate) - { - ArchiveEncoding = UseFixedEncoding ? ZipArchiveReader.DEFAULT_ENCODING : new ArchiveEncoding(Encoding.UTF8, Encoding.UTF8) - })) + var zipWriterOptions = new ZipWriterOptions(CompressionType.Deflate) + { + ArchiveEncoding = UseFixedEncoding ? ZipArchiveReader.DEFAULT_ENCODING : new ArchiveEncoding(Encoding.UTF8, Encoding.UTF8) + }; + + using (var writer = new ZipWriter(outputStream, zipWriterOptions)) { int i = 0; int fileCount = model.Files.Count(); From 38e239a435409e66cb1ccde05696b17afe2eae71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 30 Apr 2024 16:07:37 +0200 Subject: [PATCH 327/581] Expand test to cover both legacy and non-legacy export paths --- .../Skins/TestSceneBeatmapSkinResources.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index e0922c52f7..5086b64433 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Skins private BeatmapManager beatmaps { get; set; } = null!; [Test] - public void TestRetrieveJapaneseFilename() + public void TestRetrieveAndLegacyExportJapaneseFilename() { IWorkingBeatmap beatmap = null!; MemoryStream outStream = null!; @@ -46,6 +46,29 @@ namespace osu.Game.Tests.Skins AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); } + [Test] + public void TestRetrieveAndNonLegacyExportJapaneseFilename() + { + IWorkingBeatmap beatmap = null!; + MemoryStream outStream = null!; + + // Ensure importer encoding is correct + AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"japanese-filename.osz")); + AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); + + // Ensure exporter encoding is correct (round trip) + AddStep("export", () => + { + outStream = new MemoryStream(); + + new BeatmapExporter(LocalStorage) + .ExportToStream((BeatmapSetInfo)beatmap.BeatmapInfo.BeatmapSet!, outStream, null); + }); + + AddStep("import beatmap again", () => beatmap = importBeatmapFromStream(outStream)); + AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo(@"見本")) != null); + } + [Test] public void TestRetrieveOggAudio() { From ff108416d86a920dbe9f652898c59b263f2379a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 May 2024 00:05:14 +0800 Subject: [PATCH 328/581] Fix incorrect background being loaded due to async race If the API login (and thus user set) completed between `load` and `LoadComplete`, the re-fetch on user change would not yet be hooked up, causing an incorrect default background to be used instead. Of note, moving this out of async load doesn't really affect load performance as the bulk of the load operation is already scheduled and `LoadComponentAsync`ed anyway --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index a552b22c11..090e006671 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -56,10 +56,6 @@ namespace osu.Game.Screens.Backgrounds introSequence = config.GetBindable(OsuSetting.IntroSequence); AddInternal(seasonalBackgroundLoader); - - // Load first background asynchronously as part of BDL load. - currentDisplay = RNG.Next(0, background_count); - Next(); } protected override void LoadComplete() @@ -73,6 +69,9 @@ namespace osu.Game.Screens.Backgrounds introSequence.ValueChanged += _ => Scheduler.AddOnce(next); seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Scheduler.AddOnce(next); + currentDisplay = RNG.Next(0, background_count); + Next(); + // helper function required for AddOnce usage. void next() => Next(); } From 44091b1f352273c36f7545bfe44eaaeb0ef19003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 May 2024 17:33:03 +0800 Subject: [PATCH 329/581] Fix some lines still getting forgotten about --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 84f3c0d82a..3ecc29bd02 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -415,53 +415,54 @@ namespace osu.Game.Beatmaps.Formats { string[] split = line.Split(','); - if (!Enum.TryParse(split[0], out LegacyEventType type)) - throw new InvalidDataException($@"Unknown event type: {split[0]}"); // Until we have full storyboard encoder coverage, let's track any lines which aren't handled // and store them to a temporary location such that they aren't lost on editor save / export. bool lineSupportedByEncoder = false; - switch (type) + if (Enum.TryParse(split[0], out LegacyEventType type)) { - case LegacyEventType.Sprite: - // Generally, the background is the first thing defined in a beatmap file. - // In some older beatmaps, it is not present and replaced by a storyboard-level background instead. - // Allow the first sprite (by file order) to act as the background in such cases. - if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) - { - beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); + switch (type) + { + case LegacyEventType.Sprite: + // Generally, the background is the first thing defined in a beatmap file. + // In some older beatmaps, it is not present and replaced by a storyboard-level background instead. + // Allow the first sprite (by file order) to act as the background in such cases. + if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile)) + { + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]); + lineSupportedByEncoder = true; + } + + break; + + case LegacyEventType.Video: + string filename = CleanFilename(split[2]); + + // Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO + // instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported + // video extensions and handle similar to a background if it doesn't match. + if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant())) + { + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + lineSupportedByEncoder = true; + } + + break; + + case LegacyEventType.Background: + beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); lineSupportedByEncoder = true; - } + break; - break; + case LegacyEventType.Break: + double start = getOffsetTime(Parsing.ParseDouble(split[1])); + double end = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2]))); - case LegacyEventType.Video: - string filename = CleanFilename(split[2]); - - // Some very old beatmaps had incorrect type specifications for their backgrounds (ie. using 1 for VIDEO - // instead of 0 for BACKGROUND). To handle this gracefully, check the file extension against known supported - // video extensions and handle similar to a background if it doesn't match. - if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(filename).ToLowerInvariant())) - { - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.Breaks.Add(new BreakPeriod(start, end)); lineSupportedByEncoder = true; - } - - break; - - case LegacyEventType.Background: - beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]); - lineSupportedByEncoder = true; - break; - - case LegacyEventType.Break: - double start = getOffsetTime(Parsing.ParseDouble(split[1])); - double end = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2]))); - - beatmap.Breaks.Add(new BreakPeriod(start, end)); - lineSupportedByEncoder = true; - break; + break; + } } if (!lineSupportedByEncoder) From 67c0d7590a93a07cc642ecd1b03f70d6768f4638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 May 2024 19:31:39 +0800 Subject: [PATCH 330/581] Decrease alpha of delayed resume overlay as count approaches zero Allows better visibility of playfield underneath it. --- osu.Game/Screens/Play/DelayedResumeOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs index 147d48ae02..32cdabcf98 100644 --- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs +++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs @@ -170,6 +170,8 @@ namespace osu.Game.Screens.Play countdownProgress.Progress = amountTimePassed; countdownProgress.InnerRadius = progress_stroke_width / progress_size / countdownProgress.Scale.X; + Alpha = 0.2f + 0.8f * newCount / 3f; + if (countdownCount != newCount) { if (newCount > 0) From 87e814e2019d0480d638f2287f2392ec14e7a1ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 May 2024 19:34:42 +0800 Subject: [PATCH 331/581] Fix incorrect xmldoc and adjust colour provider to match `Player` --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- osu.Game/Screens/Play/DelayedResumeOverlay.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a422761800..a28b2716cb 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -529,7 +529,7 @@ namespace osu.Game.Rulesets.UI public ResumeOverlay ResumeOverlay { get; protected set; } /// - /// Whether the should be used to return the user's cursor position to its previous location after a pause. + /// Whether a should be displayed on resuming after a pause. /// /// /// Defaults to true. diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs index 32cdabcf98..3202a524a8 100644 --- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs +++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs @@ -24,8 +24,9 @@ namespace osu.Game.Screens.Play /// public partial class DelayedResumeOverlay : ResumeOverlay { - // todo: this shouldn't define its own colour provider, but nothing in Player screen does, so let's do that for now. - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + // todo: this shouldn't define its own colour provider, but nothing in DrawableRuleset guarantees this, so let's do it locally for now. + // (of note, Player does cache one but any test which uses a DrawableRuleset without Player will fail without this). + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); private const float outer_size = 200; private const float inner_size = 150; From b8209b92f61d1a4098ccf0a42c36c7b7aceeb473 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 May 2024 19:49:45 +0800 Subject: [PATCH 332/581] Add delay before delayed resume starts It was previously a bit sudden after dismissing the pause screen. Now there's a short delay before the actual countdown begins. --- osu.Game/Screens/Play/DelayedResumeOverlay.cs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/DelayedResumeOverlay.cs b/osu.Game/Screens/Play/DelayedResumeOverlay.cs index 3202a524a8..8acb94a5af 100644 --- a/osu.Game/Screens/Play/DelayedResumeOverlay.cs +++ b/osu.Game/Screens/Play/DelayedResumeOverlay.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; -using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; @@ -35,9 +34,10 @@ namespace osu.Game.Screens.Play private const double countdown_time = 2000; + private const int total_count = 3; + protected override LocalisableString Message => string.Empty; - private ScheduledDelegate? scheduledResume; private int? countdownCount; private double countdownStartTime; private bool countdownComplete; @@ -121,21 +121,17 @@ namespace osu.Game.Screens.Play innerContent.FadeIn().ScaleTo(Vector2.Zero).Then().ScaleTo(Vector2.One, 400, Easing.OutElasticHalf); countdownComponents.FadeOut().Delay(50).FadeTo(1, 100); + countdownProgress.Progress = 0; + // Reset states for various components. countdownBackground.FadeIn(); countdownText.FadeIn(); + countdownText.Text = string.Empty; countdownProgress.FadeIn().ScaleTo(1); countdownComplete = false; countdownCount = null; - countdownStartTime = Time.Current; - - scheduledResume?.Cancel(); - scheduledResume = Scheduler.AddDelayed(() => - { - countdownComplete = true; - Resume(); - }, countdown_time); + countdownStartTime = Time.Current + 200; } protected override void PopOut() @@ -153,8 +149,6 @@ namespace osu.Game.Screens.Play } else countdownProgress.FadeOut(); - - scheduledResume?.Cancel(); } protected override void Update() @@ -165,13 +159,16 @@ namespace osu.Game.Screens.Play private void updateCountdown() { - double amountTimePassed = Math.Min(countdown_time, Time.Current - countdownStartTime) / countdown_time; - int newCount = 3 - (int)Math.Floor(amountTimePassed * 3); + if (State.Value == Visibility.Hidden || countdownComplete || Time.Current < countdownStartTime) + return; + + double amountTimePassed = Math.Clamp((Time.Current - countdownStartTime) / countdown_time, 0, countdown_time); + int newCount = Math.Clamp(total_count - (int)Math.Floor(amountTimePassed * total_count), 0, total_count); countdownProgress.Progress = amountTimePassed; countdownProgress.InnerRadius = progress_stroke_width / progress_size / countdownProgress.Scale.X; - Alpha = 0.2f + 0.8f * newCount / 3f; + Alpha = 0.2f + 0.8f * newCount / total_count; if (countdownCount != newCount) { @@ -194,6 +191,12 @@ namespace osu.Game.Screens.Play } countdownCount = newCount; + + if (countdownCount == 0) + { + countdownComplete = true; + Resume(); + } } } } From f0eef329139f0dbe25fb0aa5ee534b33386f466f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 1 May 2024 15:21:39 +0200 Subject: [PATCH 333/581] Fix code quality inspection --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 3ecc29bd02..6fa78fa8e6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -415,7 +415,6 @@ namespace osu.Game.Beatmaps.Formats { string[] split = line.Split(','); - // Until we have full storyboard encoder coverage, let's track any lines which aren't handled // and store them to a temporary location such that they aren't lost on editor save / export. bool lineSupportedByEncoder = false; From 981a19f6a5c6a9b2d9ef904e9644ac82af5e7007 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:17:50 +0300 Subject: [PATCH 334/581] Disable naming inspections in p/invoke code --- osu.Desktop/NVAPI.cs | 3 +++ osu.Desktop/Windows/WindowsAssociationManager.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index 554f89a847..c1622e7cc1 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -489,6 +489,7 @@ namespace osu.Desktop public static uint Stride => (uint)Marshal.SizeOf(typeof(NvApplication)) | (2 << 16); } + // ReSharper disable InconsistentNaming internal enum NvStatus { OK = 0, // Success. Request is completed. @@ -738,4 +739,6 @@ namespace osu.Desktop OGL_THREAD_CONTROL_NUM_VALUES = 2, OGL_THREAD_CONTROL_DEFAULT = 0 } + + // ReSharper restore InconsistentNaming } diff --git a/osu.Desktop/Windows/WindowsAssociationManager.cs b/osu.Desktop/Windows/WindowsAssociationManager.cs index 11b5c19ca1..e4c070934e 100644 --- a/osu.Desktop/Windows/WindowsAssociationManager.cs +++ b/osu.Desktop/Windows/WindowsAssociationManager.cs @@ -163,6 +163,8 @@ namespace osu.Desktop.Windows [DllImport("Shell32.dll")] private static extern void SHChangeNotify(EventId wEventId, Flags uFlags, IntPtr dwItem1, IntPtr dwItem2); + // ReSharper disable InconsistentNaming + private enum EventId { /// @@ -174,9 +176,12 @@ namespace osu.Desktop.Windows private enum Flags : uint { + // ReSharper disable once InconsistentNaming SHCNF_IDLIST = 0x0000 } + // ReSharper restore InconsistentNaming + #endregion private record FileAssociation(string Extension, LocalisableString Description, string IconPath) From 02be275554beed4e450c8eca29d1cf979890d489 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:20:20 +0300 Subject: [PATCH 335/581] Disable naming inspections in country/language enums --- osu.Game/Localisation/Language.cs | 2 ++ osu.Game/Users/CountryCode.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index 711e95486f..e946779cba 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -4,6 +4,8 @@ using System.ComponentModel; using JetBrains.Annotations; +// ReSharper disable InconsistentNaming + namespace osu.Game.Localisation { [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] diff --git a/osu.Game/Users/CountryCode.cs b/osu.Game/Users/CountryCode.cs index edaa1562c7..6a0ed63648 100644 --- a/osu.Game/Users/CountryCode.cs +++ b/osu.Game/Users/CountryCode.cs @@ -6,6 +6,8 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +// ReSharper disable InconsistentNaming + namespace osu.Game.Users { /// From 2fd8950b2135edde3d63431ea68e25f951a93b5b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:22:08 +0300 Subject: [PATCH 336/581] Disable inconsistent naming in some fields of `LegacyManiaSkinConfigurationLookup` --- osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs index cacca0de23..1550fc8a47 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs @@ -67,7 +67,10 @@ namespace osu.Game.Skinning LeftStageImage, RightStageImage, BottomStageImage, + + // ReSharper disable once InconsistentNaming Hit300g, + Hit300, Hit200, Hit100, From 97fc2a5473854b403525bcc028aaf24711af58ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:20:57 +0300 Subject: [PATCH 337/581] Disable inconsistent naming in some fields of `ScoreRank` --- osu.Game/Scoring/ScoreRank.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs index 327e4191d7..957cfc9b95 100644 --- a/osu.Game/Scoring/ScoreRank.cs +++ b/osu.Game/Scoring/ScoreRank.cs @@ -35,6 +35,7 @@ namespace osu.Game.Scoring [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankSH))] [Description(@"S+")] + // ReSharper disable once InconsistentNaming SH, [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankX))] @@ -43,6 +44,7 @@ namespace osu.Game.Scoring [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankXH))] [Description(@"SS+")] + // ReSharper disable once InconsistentNaming XH, } } From 16bae4f0046f0c9bde8cdadb2a96f535c0904744 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:22:42 +0300 Subject: [PATCH 338/581] Update naming of enum fields in `StartMode` --- .../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 66acd6d1b0..5446211ced 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -486,16 +486,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Off = 0, [Description("30 seconds")] - Seconds_30 = 30, + Seconds30 = 30, [Description("1 minute")] - Seconds_60 = 60, + Seconds60 = 60, [Description("3 minutes")] - Seconds_180 = 180, + Seconds180 = 180, [Description("5 minutes")] - Seconds_300 = 300 + Seconds300 = 300 } } } From e8a63813953ac17f3f646fe02dcfdf6f404cb158 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:22:52 +0300 Subject: [PATCH 339/581] Update naming of enum fields in `ObjType` --- osu.Game/IO/Legacy/SerializationReader.cs | 76 +++++++++++------------ osu.Game/IO/Legacy/SerializationWriter.cs | 40 ++++++------ 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 2d3d5bffd5..fc61b028a4 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -123,58 +123,58 @@ namespace osu.Game.IO.Legacy switch (t) { - case ObjType.boolType: + case ObjType.BoolType: return ReadBoolean(); - case ObjType.byteType: + case ObjType.ByteType: return ReadByte(); - case ObjType.uint16Type: + case ObjType.UInt16Type: return ReadUInt16(); - case ObjType.uint32Type: + case ObjType.UInt32Type: return ReadUInt32(); - case ObjType.uint64Type: + case ObjType.UInt64Type: return ReadUInt64(); - case ObjType.sbyteType: + case ObjType.SByteType: return ReadSByte(); - case ObjType.int16Type: + case ObjType.Int16Type: return ReadInt16(); - case ObjType.int32Type: + case ObjType.Int32Type: return ReadInt32(); - case ObjType.int64Type: + case ObjType.Int64Type: return ReadInt64(); - case ObjType.charType: + case ObjType.CharType: return ReadChar(); - case ObjType.stringType: + case ObjType.StringType: return base.ReadString(); - case ObjType.singleType: + case ObjType.SingleType: return ReadSingle(); - case ObjType.doubleType: + case ObjType.DoubleType: return ReadDouble(); - case ObjType.decimalType: + case ObjType.DecimalType: return ReadDecimal(); - case ObjType.dateTimeType: + case ObjType.DateTimeType: return ReadDateTime(); - case ObjType.byteArrayType: + case ObjType.ByteArrayType: return ReadByteArray(); - case ObjType.charArrayType: + case ObjType.CharArrayType: return ReadCharArray(); - case ObjType.otherType: + case ObjType.OtherType: throw new IOException("Deserialization of arbitrary type is not supported."); default: @@ -185,25 +185,25 @@ namespace osu.Game.IO.Legacy public enum ObjType : byte { - nullType, - boolType, - byteType, - uint16Type, - uint32Type, - uint64Type, - sbyteType, - int16Type, - int32Type, - int64Type, - charType, - stringType, - singleType, - doubleType, - decimalType, - dateTimeType, - byteArrayType, - charArrayType, - otherType, - ILegacySerializableType + NullType, + BoolType, + ByteType, + UInt16Type, + UInt32Type, + UInt64Type, + SByteType, + Int16Type, + Int32Type, + Int64Type, + CharType, + StringType, + SingleType, + DoubleType, + DecimalType, + DateTimeType, + ByteArrayType, + CharArrayType, + OtherType, + LegacySerializableType } } diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs index 10572a6478..afe86cd096 100644 --- a/osu.Game/IO/Legacy/SerializationWriter.cs +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -34,11 +34,11 @@ namespace osu.Game.IO.Legacy { if (str == null) { - Write((byte)ObjType.nullType); + Write((byte)ObjType.NullType); } else { - Write((byte)ObjType.stringType); + Write((byte)ObjType.StringType); base.Write(str); } } @@ -125,94 +125,94 @@ namespace osu.Game.IO.Legacy { if (obj == null) { - Write((byte)ObjType.nullType); + Write((byte)ObjType.NullType); } else { switch (obj) { case bool boolObj: - Write((byte)ObjType.boolType); + Write((byte)ObjType.BoolType); Write(boolObj); break; case byte byteObj: - Write((byte)ObjType.byteType); + Write((byte)ObjType.ByteType); Write(byteObj); break; case ushort ushortObj: - Write((byte)ObjType.uint16Type); + Write((byte)ObjType.UInt16Type); Write(ushortObj); break; case uint uintObj: - Write((byte)ObjType.uint32Type); + Write((byte)ObjType.UInt32Type); Write(uintObj); break; case ulong ulongObj: - Write((byte)ObjType.uint64Type); + Write((byte)ObjType.UInt64Type); Write(ulongObj); break; case sbyte sbyteObj: - Write((byte)ObjType.sbyteType); + Write((byte)ObjType.SByteType); Write(sbyteObj); break; case short shortObj: - Write((byte)ObjType.int16Type); + Write((byte)ObjType.Int16Type); Write(shortObj); break; case int intObj: - Write((byte)ObjType.int32Type); + Write((byte)ObjType.Int32Type); Write(intObj); break; case long longObj: - Write((byte)ObjType.int64Type); + Write((byte)ObjType.Int64Type); Write(longObj); break; case char charObj: - Write((byte)ObjType.charType); + Write((byte)ObjType.CharType); base.Write(charObj); break; case string stringObj: - Write((byte)ObjType.stringType); + Write((byte)ObjType.StringType); base.Write(stringObj); break; case float floatObj: - Write((byte)ObjType.singleType); + Write((byte)ObjType.SingleType); Write(floatObj); break; case double doubleObj: - Write((byte)ObjType.doubleType); + Write((byte)ObjType.DoubleType); Write(doubleObj); break; case decimal decimalObj: - Write((byte)ObjType.decimalType); + Write((byte)ObjType.DecimalType); Write(decimalObj); break; case DateTime dateTimeObj: - Write((byte)ObjType.dateTimeType); + Write((byte)ObjType.DateTimeType); Write(dateTimeObj); break; case byte[] byteArray: - Write((byte)ObjType.byteArrayType); + Write((byte)ObjType.ByteArrayType); base.Write(byteArray); break; case char[] charArray: - Write((byte)ObjType.charArrayType); + Write((byte)ObjType.CharArrayType); base.Write(charArray); break; From 9dc1a58ce7a4fb00a973a3ce4498b5bff8164e16 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 17:23:27 +0300 Subject: [PATCH 340/581] Add few abbreviations from existing enums to appease naming inspection --- osu.sln.DotSettings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index dd71744bf0..51af281ac6 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -340,6 +340,7 @@ API ARGB BPM + DDKK EF FPS GC @@ -357,6 +358,8 @@ IP IPC JIT + KDDK + KKDD LTRB MD5 NS @@ -375,6 +378,7 @@ QAT BNG UI + WIP False HINT <?xml version="1.0" encoding="utf-16"?> From 30fd40efd16a651a6c00b5c89289a85ffcbe546b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 21:18:56 +0300 Subject: [PATCH 341/581] Avoid disable/restore pairs --- osu.Desktop/NVAPI.cs | 9 ++++++--- osu.Desktop/Windows/WindowsAssociationManager.cs | 8 +++----- osu.Game/Localisation/Language.cs | 4 ++-- osu.Game/Users/CountryCode.cs | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index c1622e7cc1..0b09613ba0 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -489,7 +489,7 @@ namespace osu.Desktop public static uint Stride => (uint)Marshal.SizeOf(typeof(NvApplication)) | (2 << 16); } - // ReSharper disable InconsistentNaming + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvStatus { OK = 0, // Success. Request is completed. @@ -612,6 +612,7 @@ namespace osu.Desktop FIRMWARE_REVISION_NOT_SUPPORTED = -200, // The device's firmware is not supported. } + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvSystemType { UNKNOWN = 0, @@ -619,6 +620,7 @@ namespace osu.Desktop DESKTOP = 2 } + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvGpuType { UNKNOWN = 0, @@ -626,6 +628,7 @@ namespace osu.Desktop DGPU = 2, // Discrete } + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvSettingID : uint { OGL_AA_LINE_GAMMA_ID = 0x2089BF6C, @@ -718,6 +721,7 @@ namespace osu.Desktop INVALID_SETTING_ID = 0xFFFFFFFF } + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvShimSetting : uint { SHIM_RENDERING_MODE_INTEGRATED = 0x00000000, @@ -732,6 +736,7 @@ namespace osu.Desktop SHIM_RENDERING_MODE_DEFAULT = SHIM_RENDERING_MODE_AUTO_SELECT } + [SuppressMessage("ReSharper", "InconsistentNaming")] internal enum NvThreadControlSetting : uint { OGL_THREAD_CONTROL_ENABLE = 0x00000001, @@ -739,6 +744,4 @@ namespace osu.Desktop OGL_THREAD_CONTROL_NUM_VALUES = 2, OGL_THREAD_CONTROL_DEFAULT = 0 } - - // ReSharper restore InconsistentNaming } diff --git a/osu.Desktop/Windows/WindowsAssociationManager.cs b/osu.Desktop/Windows/WindowsAssociationManager.cs index e4c070934e..b32c01433d 100644 --- a/osu.Desktop/Windows/WindowsAssociationManager.cs +++ b/osu.Desktop/Windows/WindowsAssociationManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -163,8 +164,7 @@ namespace osu.Desktop.Windows [DllImport("Shell32.dll")] private static extern void SHChangeNotify(EventId wEventId, Flags uFlags, IntPtr dwItem1, IntPtr dwItem2); - // ReSharper disable InconsistentNaming - + [SuppressMessage("ReSharper", "InconsistentNaming")] private enum EventId { /// @@ -174,14 +174,12 @@ namespace osu.Desktop.Windows SHCNE_ASSOCCHANGED = 0x08000000 } + [SuppressMessage("ReSharper", "InconsistentNaming")] private enum Flags : uint { - // ReSharper disable once InconsistentNaming SHCNF_IDLIST = 0x0000 } - // ReSharper restore InconsistentNaming - #endregion private record FileAssociation(string Extension, LocalisableString Description, string IconPath) diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index e946779cba..4e1fc3a474 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; -// ReSharper disable InconsistentNaming - namespace osu.Game.Localisation { + [SuppressMessage("ReSharper", "InconsistentNaming")] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public enum Language { diff --git a/osu.Game/Users/CountryCode.cs b/osu.Game/Users/CountryCode.cs index 6a0ed63648..59fcd5d625 100644 --- a/osu.Game/Users/CountryCode.cs +++ b/osu.Game/Users/CountryCode.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -// ReSharper disable InconsistentNaming - namespace osu.Game.Users { /// /// Matches `osu_countries` database table. /// [JsonConverter(typeof(StringEnumConverter))] + [SuppressMessage("ReSharper", "InconsistentNaming")] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public enum CountryCode { From eb45a406e153e9f533f51efaa95d30bb5bc0abe8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 23:00:24 +0300 Subject: [PATCH 342/581] Add failing test case --- .../Formats/LegacyScoreEncoderTest.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreEncoderTest.cs index 806f538249..1e57bd76cf 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreEncoderTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestCase(1, 3)] [TestCase(1, 0)] [TestCase(0, 3)] - public void CatchMergesFruitAndDropletMisses(int missCount, int largeTickMissCount) + public void TestCatchMergesFruitAndDropletMisses(int missCount, int largeTickMissCount) { var ruleset = new CatchRuleset().RulesetInfo; var scoreInfo = TestResources.CreateTestScoreInfo(ruleset); @@ -41,7 +41,22 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void ScoreWithMissIsNotPerfect() + public void TestFailPreserved() + { + var ruleset = new OsuRuleset().RulesetInfo; + var scoreInfo = TestResources.CreateTestScoreInfo(); + var beatmap = new TestBeatmap(ruleset); + + scoreInfo.Rank = ScoreRank.F; + + var score = new Score { ScoreInfo = scoreInfo }; + var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap); + + Assert.That(decodedAfterEncode.ScoreInfo.Rank, Is.EqualTo(ScoreRank.F)); + } + + [Test] + public void TestScoreWithMissIsNotPerfect() { var ruleset = new OsuRuleset().RulesetInfo; var scoreInfo = TestResources.CreateTestScoreInfo(ruleset); From 0106f1fe3ead4d5bdbef25c1e0262b3988131894 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 23:31:23 +0300 Subject: [PATCH 343/581] Preserve score rank on encode/decode --- osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs | 6 ++++++ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs index afdcef1d21..32c18b3af2 100644 --- a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Scoring; @@ -38,6 +39,10 @@ namespace osu.Game.Scoring.Legacy [JsonProperty("client_version")] public string ClientVersion = string.Empty; + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank? Rank; + public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo { OnlineID = score.OnlineID, @@ -45,6 +50,7 @@ namespace osu.Game.Scoring.Legacy Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(), MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(), ClientVersion = score.ClientVersion, + Rank = score.Rank, }; } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 65e2c02655..b0d7087ed1 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -40,6 +40,7 @@ namespace osu.Game.Scoring.Legacy }; WorkingBeatmap workingBeatmap; + ScoreRank? decodedRank = null; using (SerializationReader sr = new SerializationReader(stream)) { @@ -129,6 +130,7 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics; score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray(); score.ScoreInfo.ClientVersion = readScore.ClientVersion; + decodedRank = readScore.Rank; }); } } @@ -140,6 +142,9 @@ namespace osu.Game.Scoring.Legacy StandardisedScoreMigrationTools.UpdateFromLegacy(score.ScoreInfo, workingBeatmap); + if (decodedRank != null) + score.ScoreInfo.Rank = decodedRank.Value; + // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo; From 4ffeb5b469b86bdfbf80dde018fc3c9d32ec4e4b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 1 May 2024 23:57:21 +0300 Subject: [PATCH 344/581] Resolve post-merge-conflict issues --- .../Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs | 4 ++-- .../Visual/Gameplay/TestSceneStoryboardWithIntro.cs | 2 +- osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs | 2 +- osu.Game/Storyboards/StoryboardVideo.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs index dd6f833ec5..800857c973 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs @@ -85,8 +85,8 @@ namespace osu.Game.Tests.Visual.Gameplay if (scaleTransformProvided) { - sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current + 1000, 1, 2); - sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current + 1000, Time.Current + 2000, 2, 1); + sprite.Commands.AddScale(Easing.None, Time.Current, Time.Current + 1000, 1, 2); + sprite.Commands.AddScale(Easing.None, Time.Current + 1000, Time.Current + 2000, 2, 1); } layer.Elements.Clear(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs index 502a0de616..ee6a6938a9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithIntro.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var storyboard = new Storyboard(); var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); - sprite.TimelineGroup.Alpha.Add(Easing.None, startTime, 0, 0, 1); + sprite.Commands.AddAlpha(Easing.None, startTime, 0, 0, 1); storyboard.GetLayer("Background").Add(sprite); return storyboard; } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index f2454be190..08c9459042 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -25,7 +25,7 @@ namespace osu.Game.Storyboards.Drawables // This allows scaling based on the video's absolute size. // // If not specified we take up the full available space. - bool useRelative = !video.TimelineGroup.Scale.HasCommands; + bool useRelative = !video.Commands.Scale.Any(); RelativeSizeAxes = useRelative ? Axes.Both : Axes.None; AutoSizeAxes = useRelative ? Axes.None : Axes.Both; diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs index 5573162d26..14189a1a6c 100644 --- a/osu.Game/Storyboards/StoryboardVideo.cs +++ b/osu.Game/Storyboards/StoryboardVideo.cs @@ -14,7 +14,7 @@ namespace osu.Game.Storyboards { // This is just required to get a valid StartTime based on the incoming offset. // Actual fades are handled inside DrawableStoryboardVideo for now. - TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 0); + Commands.AddAlpha(Easing.None, offset, offset, 0, 0); } public override Drawable CreateDrawable() => new DrawableStoryboardVideo(this); From 6d6f165884ab31f4aec606bae0ce6ee3eba4f9fd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 2 May 2024 00:00:56 +0300 Subject: [PATCH 345/581] Make video sprites flippable and vector-scalable to fix type constraint errors Stable supports these on videos already from a quick read on code (since videos are generally a subclass of sprites). --- .../Drawables/DrawableStoryboardVideo.cs | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index 08c9459042..329564a345 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -1,11 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.IO; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Video; +using osu.Framework.Utils; +using osuTK; namespace osu.Game.Storyboards.Drawables { @@ -13,7 +18,7 @@ namespace osu.Game.Storyboards.Drawables { public readonly StoryboardVideo Video; - private Video? drawableVideo; + private DrawableVideo? drawableVideo; public override bool RemoveWhenNotAlive => false; @@ -42,7 +47,7 @@ namespace osu.Game.Storyboards.Drawables if (stream == null) return; - InternalChild = drawableVideo = new Video(stream, false) + InternalChild = drawableVideo = new DrawableVideo(stream, false) { RelativeSizeAxes = RelativeSizeAxes, FillMode = FillMode.Fill, @@ -70,5 +75,65 @@ namespace osu.Game.Storyboards.Drawables drawableVideo.FadeOut(500); } } + + private partial class DrawableVideo : Video, IFlippable, IVectorScalable + { + private bool flipH; + + public bool FlipH + { + get => flipH; + set + { + if (flipH == value) + return; + + flipH = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + private bool flipV; + + public bool FlipV + { + get => flipV; + set + { + if (flipV == value) + return; + + flipV = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + private Vector2 vectorScale = Vector2.One; + + public Vector2 VectorScale + { + get => vectorScale; + set + { + if (vectorScale == value) + return; + + if (!Validation.IsFinite(value)) throw new ArgumentException($@"{nameof(VectorScale)} must be finite, but is {value}."); + + vectorScale = value; + Invalidate(Invalidation.MiscGeometry); + } + } + + protected override Vector2 DrawScale + => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y) * VectorScale; + + public override Anchor Origin => StoryboardExtensions.AdjustOrigin(base.Origin, VectorScale, FlipH, FlipV); + + public DrawableVideo(Stream stream, bool startAtCurrentTime = true) + : base(stream, startAtCurrentTime) + { + } + } } } From b1aff91bba0d9aefdfb09d0e40e0b79673a691f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 2 May 2024 00:31:48 +0300 Subject: [PATCH 346/581] Use throw helper methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs index a886998679..fe334ad608 100644 --- a/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs +++ b/osu.Game/Storyboards/Commands/StoryboardLoopingGroup.cs @@ -23,7 +23,7 @@ namespace osu.Game.Storyboards.Commands /// The number of times the loop should repeat. Should be greater than zero. Zero means a single playback. public StoryboardLoopingGroup(double startTime, int repeatCount) { - if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); + ArgumentOutOfRangeException.ThrowIfNegative(repeatCount); loopStartTime = startTime; TotalIterations = repeatCount + 1; From 1eac34667d8c91679889475d17c8030e3bb85171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 May 2024 08:02:55 +0200 Subject: [PATCH 347/581] Fix test failure --- osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index 43e471320e..383c08c10f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -352,6 +352,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [HitResult.Great] = 200, [HitResult.LargeTickHit] = 1, }; + scoreInfo.Rank = ScoreRank.A; var beatmap = new TestBeatmap(ruleset); var score = new Score From f9ef689492775d7c419fc1640621fe6cdf9f79e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 2 May 2024 15:36:40 +0800 Subject: [PATCH 348/581] Don't hide playfield layer with HUD --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 2ec2a011a6..9d7a05bc90 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Play }, }; - hideTargets = new List { mainComponents, playfieldComponents, topRightElements }; + hideTargets = new List { mainComponents, topRightElements }; if (rulesetComponents != null) hideTargets.Add(rulesetComponents); From 093c25539cd85911f4a06ba780e647737c7ac676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 May 2024 13:59:40 +0200 Subject: [PATCH 349/581] Add failing test case --- .../Editor/TestSceneOsuEditorGrids.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index d14e593587..b720eb148b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Tests.Visual; @@ -54,6 +55,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); } + [Test] + public void TestDistanceSnapAdjustDoesNotHideTheGrid() + { + double distanceSnap = double.PositiveInfinity; + + AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + AddStep("store distance snap", () => distanceSnap = this.ChildrenOfType().First().DistanceSpacingMultiplier.Value); + + AddStep("increase distance", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.PressKey(Key.ControlLeft); + InputManager.ScrollVerticalBy(1); + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.AltLeft); + }); + + AddUntilStep("distance snap increased", () => this.ChildrenOfType().First().DistanceSpacingMultiplier.Value, () => Is.GreaterThan(distanceSnap)); + AddUntilStep("distance snap grid still visible", () => this.ChildrenOfType().Any()); + } + [Test] public void TestGridSnapMomentaryToggle() { From 14658824e712a9aea3d1e6697d169a6e0b873201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 May 2024 14:11:44 +0200 Subject: [PATCH 350/581] Adjust distance snap grid momentary toggle logic to not hide it on spacing adjust --- .../Rulesets/Edit/ComposerDistanceSnapProvider.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index 62ad2ce7e9..b9850a94a3 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Edit public readonly Bindable DistanceSnapToggle = new Bindable(); private bool distanceSnapMomentary; + private TernaryState? distanceSnapStateBeforeMomentaryToggle; private EditorToolboxGroup? toolboxGroup; @@ -213,10 +214,19 @@ namespace osu.Game.Rulesets.Edit { bool altPressed = key.AltPressed; - if (altPressed != distanceSnapMomentary) + if (altPressed && !distanceSnapMomentary) { - distanceSnapMomentary = altPressed; + distanceSnapStateBeforeMomentaryToggle = DistanceSnapToggle.Value; DistanceSnapToggle.Value = DistanceSnapToggle.Value == TernaryState.False ? TernaryState.True : TernaryState.False; + distanceSnapMomentary = true; + } + + if (!altPressed && distanceSnapMomentary) + { + Debug.Assert(distanceSnapStateBeforeMomentaryToggle != null); + DistanceSnapToggle.Value = distanceSnapStateBeforeMomentaryToggle.Value; + distanceSnapStateBeforeMomentaryToggle = null; + distanceSnapMomentary = false; } } From 6000ffed2a7eb000efb4ea045055d620be2dcbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 May 2024 14:15:46 +0200 Subject: [PATCH 351/581] Add extended test coverage for intended behaviour --- .../Editor/TestSceneOsuEditorGrids.cs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs index b720eb148b..5798869210 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs @@ -53,10 +53,17 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + + AddStep("enable distance snap grid", () => InputManager.Key(Key.T)); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft)); + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); } [Test] - public void TestDistanceSnapAdjustDoesNotHideTheGrid() + public void TestDistanceSnapAdjustDoesNotHideTheGridIfStartingEnabled() { double distanceSnap = double.PositiveInfinity; @@ -79,6 +86,34 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddUntilStep("distance snap grid still visible", () => this.ChildrenOfType().Any()); } + [Test] + public void TestDistanceSnapAdjustShowsGridMomentarilyIfStartingDisabled() + { + double distanceSnap = double.PositiveInfinity; + + AddStep("select second object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.ElementAt(1))); + AddUntilStep("distance snap grid hidden", () => !this.ChildrenOfType().Any()); + AddStep("store distance snap", () => distanceSnap = this.ChildrenOfType().First().DistanceSpacingMultiplier.Value); + + AddStep("start increasing distance", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.PressKey(Key.ControlLeft); + }); + + AddUntilStep("distance snap grid visible", () => this.ChildrenOfType().Any()); + + AddStep("finish increasing distance", () => + { + InputManager.ScrollVerticalBy(1); + InputManager.ReleaseKey(Key.ControlLeft); + InputManager.ReleaseKey(Key.AltLeft); + }); + + AddUntilStep("distance snap increased", () => this.ChildrenOfType().First().DistanceSpacingMultiplier.Value, () => Is.GreaterThan(distanceSnap)); + AddUntilStep("distance snap hidden in the end", () => !this.ChildrenOfType().Any()); + } + [Test] public void TestGridSnapMomentaryToggle() { From 802666e621254fc4574f6c6db7fec2a1dd9f9548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 2 May 2024 16:08:09 +0200 Subject: [PATCH 352/581] Update local metadata lookup cache more often As proposed in https://github.com/ppy/osu/issues/27332#issuecomment-1962308306. --- .../LocalCachedBeatmapMetadataSource.cs | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/LocalCachedBeatmapMetadataSource.cs b/osu.Game/Beatmaps/LocalCachedBeatmapMetadataSource.cs index 3f93c32283..27bc803449 100644 --- a/osu.Game/Beatmaps/LocalCachedBeatmapMetadataSource.cs +++ b/osu.Game/Beatmaps/LocalCachedBeatmapMetadataSource.cs @@ -44,11 +44,34 @@ namespace osu.Game.Beatmaps this.storage = storage; - // avoid downloading / using cache for unit tests. - if (!DebugUtils.IsNUnitRunning && !storage.Exists(cache_database_name)) + if (shouldFetchCache()) prepareLocalCache(); } + private bool shouldFetchCache() + { + // avoid downloading / using cache for unit tests. + if (DebugUtils.IsNUnitRunning) + return false; + + if (!storage.Exists(cache_database_name)) + { + log(@"Fetching local cache because it does not exist."); + return true; + } + + // periodically update the cache to include newer beatmaps. + var fileInfo = new FileInfo(storage.GetFullPath(cache_database_name)); + + if (fileInfo.LastWriteTime < DateTime.Now.AddMonths(-1)) + { + log($@"Refetching local cache because it was last written to on {fileInfo.LastWriteTime}."); + return true; + } + + return false; + } + public bool Available => // no download in progress. cacheDownloadRequest == null @@ -124,6 +147,8 @@ namespace osu.Game.Beatmaps private void prepareLocalCache() { + bool isRefetch = storage.Exists(cache_database_name); + string cacheFilePath = storage.GetFullPath(cache_database_name); string compressedCacheFilePath = $@"{cacheFilePath}.bz2"; @@ -132,9 +157,15 @@ namespace osu.Game.Beatmaps cacheDownloadRequest.Failed += ex => { File.Delete(compressedCacheFilePath); - File.Delete(cacheFilePath); - Logger.Log($@"{nameof(BeatmapUpdaterMetadataLookup)}'s online cache download failed: {ex}", LoggingTarget.Database); + // don't clobber the cache when refetching if the download didn't succeed. seems excessive. + // consequently, also null the download request to allow the existing cache to be used (see `Available`). + if (isRefetch) + cacheDownloadRequest = null; + else + File.Delete(cacheFilePath); + + log($@"Online cache download failed: {ex}"); }; cacheDownloadRequest.Finished += () => @@ -143,15 +174,22 @@ namespace osu.Game.Beatmaps { using (var stream = File.OpenRead(cacheDownloadRequest.Filename)) using (var outStream = File.OpenWrite(cacheFilePath)) - using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) - bz2.CopyTo(outStream); + { + // ensure to clobber any and all existing data to avoid accidental corruption. + outStream.SetLength(0); + + using (var bz2 = new BZip2Stream(stream, CompressionMode.Decompress, false)) + bz2.CopyTo(outStream); + } // set to null on completion to allow lookups to begin using the new source cacheDownloadRequest = null; + log(@"Local cache fetch completed successfully."); } catch (Exception ex) { - Logger.Log($@"{nameof(LocalCachedBeatmapMetadataSource)}'s online cache extraction failed: {ex}", LoggingTarget.Database); + log($@"Online cache extraction failed: {ex}"); + // at this point clobber the cache regardless of whether we're refetching, because by this point who knows what state the cache file is in. File.Delete(cacheFilePath); } finally @@ -173,6 +211,9 @@ namespace osu.Game.Beatmaps }); } + private static void log(string message) + => Logger.Log($@"[{nameof(LocalCachedBeatmapMetadataSource)}] {message}", LoggingTarget.Database); + private void logForModel(BeatmapSetInfo set, string message) => RealmArchiveModelImporter.LogForModel(set, $@"[{nameof(LocalCachedBeatmapMetadataSource)}] {message}"); From f534c4aadab0a274674bd5b077e4acea797f177e Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Thu, 2 May 2024 18:42:35 +0200 Subject: [PATCH 353/581] Initial implementation --- .../Input/Bindings/GlobalActionContainer.cs | 8 ++ .../GlobalActionKeyBindingStrings.cs | 10 ++ osu.Game/Screens/Select/SongSelect.cs | 97 ++++++++++++++++++- 3 files changed, 113 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 296232d9ea..ff7d9f0a6d 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -134,6 +134,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), new KeyBinding(InputKey.BackSpace, GlobalAction.DeselectAllMods), + new KeyBinding(InputKey.PageUp, GlobalAction.IncreaseSpeed), // Not working with minus and other keys.... + new KeyBinding(InputKey.PageDown, GlobalAction.DecreaseSpeed), }; public IEnumerable AudioControlKeyBindings => new[] @@ -364,5 +366,11 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))] EditorToggleRotateControl, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseSpeed))] + IncreaseSpeed, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseSpeed))] + DecreaseSpeed, } } diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 8356c480dd..40fe4064ed 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -349,6 +349,16 @@ namespace osu.Game.Localisation /// public static LocalisableString EditorToggleRotateControl => new TranslatableString(getKey(@"editor_toggle_rotate_control"), @"Toggle rotate control"); + /// + /// "Increase Speed" + /// + public static LocalisableString IncreaseSpeed => new TranslatableString(getKey(@"increase_speed"), @"Increase Speed"); + + /// + /// "Decrease Speed" + /// + public static LocalisableString DecreaseSpeed => new TranslatableString(getKey(@"decrease_speed"), @"Decrease Speed"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 34ee0ae4e8..f2036f017d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -755,7 +755,95 @@ namespace osu.Game.Screens.Select return false; } - + public void IncreaseSpeed() + { + // find way of grabbing all types of ModSpeedAdjust + var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod.Acronym == "DT" || pair.Mod.Acronym == "NC" || pair.Mod.Acronym == "HT" || pair.Mod.Acronym == "DC"); + var stateDoubleTime = ModSelect.AllAvailableMods.First(pair => pair.Mod.Acronym == "DT"); + bool oneActive = false; + double newRate = 1.05d; + foreach (var state in rateAdjustStates) + { + ModRateAdjust mod = (ModRateAdjust)state.Mod; + if (state.Active.Value) + { + oneActive = true; + newRate = mod.SpeedChange.Value + 0.05d; + if (mod.Acronym == "DT" || mod.Acronym == "NC") + { + mod.SpeedChange.Value = newRate; + return; + } + else + { + if (newRate == 1.0d) + { + state.Active.Value = false; + } + if (newRate > 1d) + { + state.Active.Value = false; + stateDoubleTime.Active.Value = true; + ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; + return; + } + if (newRate < 1d) + { + mod.SpeedChange.Value = newRate; + } + } + } + } + if (!oneActive) + { + stateDoubleTime.Active.Value = true; + ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; + } + } + public void DecreaseSpeed() + { + var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod.Acronym == "DT" || pair.Mod.Acronym == "NC" || pair.Mod.Acronym == "HT" || pair.Mod.Acronym == "DC"); + var stateHalfTime = ModSelect.AllAvailableMods.First(pair => pair.Mod.Acronym == "HT"); + bool oneActive = false; + double newRate = 0.95d; + foreach (var state in rateAdjustStates) + { + ModRateAdjust mod = (ModRateAdjust)state.Mod; + if (state.Active.Value) + { + oneActive = true; + newRate = mod.SpeedChange.Value - 0.05d; + if (mod.Acronym == "HT" || mod.Acronym == "DC") + { + mod.SpeedChange.Value = newRate; + return; + } + else + { + if (newRate == 1.0d) + { + state.Active.Value = false; + } + if (newRate < 1d) + { + state.Active.Value = false; + stateHalfTime.Active.Value = true; + ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; + return; + } + if (newRate > 1d) + { + mod.SpeedChange.Value = newRate; + } + } + } + } + if (!oneActive) + { + stateHalfTime.Active.Value = true; + ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; + } + } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -955,12 +1043,17 @@ namespace osu.Game.Screens.Select return false; if (!this.IsCurrentScreen()) return false; - switch (e.Action) { case GlobalAction.Select: FinaliseSelection(); return true; + case GlobalAction.IncreaseSpeed: + IncreaseSpeed(); + return true; + case GlobalAction.DecreaseSpeed: + DecreaseSpeed(); + return true; } return false; From 5c21a0330addcf81db97e7ec157510d894cbd6a0 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Thu, 2 May 2024 19:05:07 +0200 Subject: [PATCH 354/581] F1 also does not work with minus in song select, same behaviour --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index ff7d9f0a6d..204ecb9061 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -134,7 +134,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), new KeyBinding(InputKey.BackSpace, GlobalAction.DeselectAllMods), - new KeyBinding(InputKey.PageUp, GlobalAction.IncreaseSpeed), // Not working with minus and other keys.... + new KeyBinding(InputKey.PageUp, GlobalAction.IncreaseSpeed), new KeyBinding(InputKey.PageDown, GlobalAction.DecreaseSpeed), }; From 7527ddbc6865c35e26a7c4deef850e0d4e93d786 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Thu, 2 May 2024 19:05:43 +0200 Subject: [PATCH 355/581] Comment, make code more readable, functions are now private --- osu.Game/Screens/Select/SongSelect.cs | 133 ++++++++++++-------------- 1 file changed, 60 insertions(+), 73 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index f2036f017d..5de4a7a9a3 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -755,93 +755,80 @@ namespace osu.Game.Screens.Select return false; } - public void IncreaseSpeed() + private void increaseSpeed() { - // find way of grabbing all types of ModSpeedAdjust - var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod.Acronym == "DT" || pair.Mod.Acronym == "NC" || pair.Mod.Acronym == "HT" || pair.Mod.Acronym == "DC"); - var stateDoubleTime = ModSelect.AllAvailableMods.First(pair => pair.Mod.Acronym == "DT"); - bool oneActive = false; - double newRate = 1.05d; - foreach (var state in rateAdjustStates) - { - ModRateAdjust mod = (ModRateAdjust)state.Mod; - if (state.Active.Value) - { - oneActive = true; - newRate = mod.SpeedChange.Value + 0.05d; - if (mod.Acronym == "DT" || mod.Acronym == "NC") - { - mod.SpeedChange.Value = newRate; - return; - } - else - { - if (newRate == 1.0d) - { - state.Active.Value = false; - } - if (newRate > 1d) - { - state.Active.Value = false; - stateDoubleTime.Active.Value = true; - ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; - return; - } - if (newRate < 1d) - { - mod.SpeedChange.Value = newRate; - } - } - } - } - if (!oneActive) + var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); + var stateDoubleTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModDoubleTime); + bool rateModActive = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust && pair.Active.Value).Count() > 0; + double stepSize = 0.05d; + double newRate = 1d + stepSize; + // If no mod rateAdjust mod is currently active activate DoubleTime with speed newRate + if (!rateModActive) { stateDoubleTime.Active.Value = true; ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; + return; } - } - public void DecreaseSpeed() - { - var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod.Acronym == "DT" || pair.Mod.Acronym == "NC" || pair.Mod.Acronym == "HT" || pair.Mod.Acronym == "DC"); - var stateHalfTime = ModSelect.AllAvailableMods.First(pair => pair.Mod.Acronym == "HT"); - bool oneActive = false; - double newRate = 0.95d; + // Find current active rateAdjust mod and modify speed, enable DoubleTime if necessary foreach (var state in rateAdjustStates) { ModRateAdjust mod = (ModRateAdjust)state.Mod; - if (state.Active.Value) + if (!state.Active.Value) continue; + newRate = mod.SpeedChange.Value + stepSize; + if (mod.Acronym == "DT" || mod.Acronym == "NC") + mod.SpeedChange.Value = newRate; + else { - oneActive = true; - newRate = mod.SpeedChange.Value - 0.05d; - if (mod.Acronym == "HT" || mod.Acronym == "DC") + if (newRate == 1.0d) + state.Active.Value = false; + if (newRate > 1d) { + state.Active.Value = false; + stateDoubleTime.Active.Value = true; + ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; + break; + } + if (newRate < 1d) mod.SpeedChange.Value = newRate; - return; - } - else - { - if (newRate == 1.0d) - { - state.Active.Value = false; - } - if (newRate < 1d) - { - state.Active.Value = false; - stateHalfTime.Active.Value = true; - ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; - return; - } - if (newRate > 1d) - { - mod.SpeedChange.Value = newRate; - } - } } } - if (!oneActive) + } + private void decreaseSpeed() + { + var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); + var stateHalfTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModHalfTime); + bool rateModActive = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust && pair.Active.Value).Count() > 0; + double stepSize = 0.05d; + double newRate = 1d - stepSize; + // If no mod rateAdjust mod is currently active activate HalfTime with speed newRate + if (!rateModActive) { stateHalfTime.Active.Value = true; ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; + return; + } + // Find current active rateAdjust mod and modify speed, enable HalfTime if necessary + foreach (var state in rateAdjustStates) + { + ModRateAdjust mod = (ModRateAdjust)state.Mod; + if (!state.Active.Value) continue; + newRate = mod.SpeedChange.Value - stepSize; + if (mod.Acronym == "HT" || mod.Acronym == "DC") + mod.SpeedChange.Value = newRate; + else + { + if (newRate == 1.0d) + state.Active.Value = false; + if (newRate < 1d) + { + state.Active.Value = false; + stateHalfTime.Active.Value = true; + ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; + break; + } + if (newRate > 1d) + mod.SpeedChange.Value = newRate; + } } } protected override void Dispose(bool isDisposing) @@ -1049,10 +1036,10 @@ namespace osu.Game.Screens.Select FinaliseSelection(); return true; case GlobalAction.IncreaseSpeed: - IncreaseSpeed(); + increaseSpeed(); return true; case GlobalAction.DecreaseSpeed: - DecreaseSpeed(); + decreaseSpeed(); return true; } From 588badf29216782699ae359700d6449e82638187 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Thu, 2 May 2024 19:22:39 +0200 Subject: [PATCH 356/581] Fix Formatting --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index f6bc002e32..5dacb6db4d 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -416,7 +416,7 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseSpeed))] DecreaseSpeed, - + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))] IncreaseOffset, From 4b5ea6bd0bc46cf37612efa0de3c81f3d4fe52bc Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Thu, 2 May 2024 19:41:00 +0200 Subject: [PATCH 357/581] Fix Code Inspection --- osu.Game/Screens/Select/SongSelect.cs | 36 +++++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 73ce029d3f..de0f24aa90 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -808,13 +808,15 @@ namespace osu.Game.Screens.Select return false; } + private void increaseSpeed() { var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); var stateDoubleTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModDoubleTime); - bool rateModActive = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust && pair.Active.Value).Count() > 0; - double stepSize = 0.05d; - double newRate = 1d + stepSize; + bool rateModActive = ModSelect.AllAvailableMods.Count(pair => pair.Mod is ModRateAdjust && pair.Active.Value) > 0; + const double stepsize = 0.05d; + double newRate = 1d + stepsize; + // If no mod rateAdjust mod is currently active activate DoubleTime with speed newRate if (!rateModActive) { @@ -822,18 +824,23 @@ namespace osu.Game.Screens.Select ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; return; } + // Find current active rateAdjust mod and modify speed, enable DoubleTime if necessary foreach (var state in rateAdjustStates) { ModRateAdjust mod = (ModRateAdjust)state.Mod; + if (!state.Active.Value) continue; - newRate = mod.SpeedChange.Value + stepSize; + + newRate = mod.SpeedChange.Value + stepsize; + if (mod.Acronym == "DT" || mod.Acronym == "NC") mod.SpeedChange.Value = newRate; else { if (newRate == 1.0d) state.Active.Value = false; + if (newRate > 1d) { state.Active.Value = false; @@ -841,18 +848,21 @@ namespace osu.Game.Screens.Select ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; break; } + if (newRate < 1d) mod.SpeedChange.Value = newRate; } } } + private void decreaseSpeed() { var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); var stateHalfTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModHalfTime); - bool rateModActive = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust && pair.Active.Value).Count() > 0; - double stepSize = 0.05d; - double newRate = 1d - stepSize; + bool rateModActive = ModSelect.AllAvailableMods.Count(pair => pair.Mod is ModRateAdjust && pair.Active.Value) > 0; + const double stepsize = 0.05d; + double newRate = 1d - stepsize; + // If no mod rateAdjust mod is currently active activate HalfTime with speed newRate if (!rateModActive) { @@ -860,18 +870,23 @@ namespace osu.Game.Screens.Select ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; return; } + // Find current active rateAdjust mod and modify speed, enable HalfTime if necessary foreach (var state in rateAdjustStates) { ModRateAdjust mod = (ModRateAdjust)state.Mod; + if (!state.Active.Value) continue; - newRate = mod.SpeedChange.Value - stepSize; + + newRate = mod.SpeedChange.Value - stepsize; + if (mod.Acronym == "HT" || mod.Acronym == "DC") mod.SpeedChange.Value = newRate; else { if (newRate == 1.0d) state.Active.Value = false; + if (newRate < 1d) { state.Active.Value = false; @@ -879,11 +894,13 @@ namespace osu.Game.Screens.Select ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; break; } + if (newRate > 1d) mod.SpeedChange.Value = newRate; } } } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -1086,14 +1103,17 @@ namespace osu.Game.Screens.Select return false; if (!this.IsCurrentScreen()) return false; + switch (e.Action) { case GlobalAction.Select: FinaliseSelection(); return true; + case GlobalAction.IncreaseSpeed: increaseSpeed(); return true; + case GlobalAction.DecreaseSpeed: decreaseSpeed(); return true; From 2f075e32479542e8b3dc0abd0ae3c93c4f0dae9f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 2 May 2024 17:01:40 -0700 Subject: [PATCH 358/581] Apply half margin of tolerance on both sides before text scrolls --- .../TestSceneNowPlayingOverlay.cs | 22 ++++++++++++++----- osu.Game/Overlays/NowPlayingOverlay.cs | 4 +++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index 40e0d9250d..1670741cbd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -45,16 +45,28 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestLongMetadata() { - AddStep(@"set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap + AddStep(@"set metadata within tolerance", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap { Metadata = { - Artist = "very very very very very very very very very very very long artist", - ArtistUnicode = "very very very very very very very very very very very long artist", - Title = "very very very very very very very very very very very long title", - TitleUnicode = "very very very very very very very very very very very long title", + Artist = "very very very very very very very very very very verry long artist", + ArtistUnicode = "very very very very very very very very very very verry long artist", + Title = "very very very very very verry long title", + TitleUnicode = "very very very very very verry long title", } })); + + AddStep(@"set metadata outside bounds", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + Metadata = + { + Artist = "very very very very very very very very very very verrry long artist", + ArtistUnicode = "very very very very very very very very very very verrry long artist", + Title = "very very very very very verrry long title", + TitleUnicode = "very very very very very verrry long title", + } + })); + AddStep(@"show", () => nowPlayingOverlay.Show()); } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index be405257ca..b1f72fd792 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; @@ -533,7 +534,8 @@ namespace osu.Game.Overlays float textOverflowWidth = mainSpriteText.Width - player_width; - if (textOverflowWidth > 0) + // apply half margin of tolerance on both sides before the text scrolls + if (textOverflowWidth > margin) { fillerSpriteText.Alpha = 1; fillerSpriteText.Text = text; From 269077f85496142f470b3fce4306ac33ac8216ea Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 2 May 2024 17:04:22 -0700 Subject: [PATCH 359/581] Only support centre anchors --- osu.Game/Overlays/NowPlayingOverlay.cs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index b1f72fd792..7329a3b404 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -540,24 +540,8 @@ namespace osu.Game.Overlays fillerSpriteText.Alpha = 1; fillerSpriteText.Text = text; - float initialX; - float targetX; - - if (Anchor.HasFlagFast(Anchor.x0)) - { - initialX = 0; - targetX = -mainSpriteText.Width; - } - else if (Anchor.HasFlagFast(Anchor.x1)) - { - initialX = (textOverflowWidth + mainSpriteText.Width) / 2; - targetX = (textOverflowWidth - mainSpriteText.Width) / 2; - } - else // Anchor.x2 - { - initialX = textOverflowWidth + mainSpriteText.Width; - targetX = textOverflowWidth; - } + float initialX = (textOverflowWidth + mainSpriteText.Width) / 2; + float targetX = (textOverflowWidth - mainSpriteText.Width) / 2; this.MoveToX(initialX) .Delay(initial_move_delay) From 381ddb067618abb7b7cfceed944669e346b1bdf3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 2 May 2024 17:05:12 -0700 Subject: [PATCH 360/581] Fix weird formatting --- osu.Game/Overlays/NowPlayingOverlay.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 7329a3b404..1488f59246 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -494,14 +494,13 @@ namespace osu.Game.Overlays public FontUsage Font { - set => - Schedule(() => - { - mainSpriteText.Font = value; - fillerSpriteText.Font = value; + set => Schedule(() => + { + mainSpriteText.Font = value; + fillerSpriteText.Font = value; - updateText(); - }); + updateText(); + }); } public ScrollingTextContainer() From c15a68507152b0710b90a4fa947cb5b994258bf9 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 2 May 2024 17:07:49 -0700 Subject: [PATCH 361/581] Remove unused usings --- osu.Game/Overlays/NowPlayingOverlay.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 1488f59246..8f97ce50cc 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -6,10 +6,8 @@ using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; From aa4d16bdb873d7296b899cee7b7491ffdf5cd6ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 12:13:52 +0800 Subject: [PATCH 362/581] Fix beatmap listing cards being far too large --- osu.Game/Overlays/BeatmapListingOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index a645683c5f..9b2f26e8ae 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -198,6 +198,7 @@ namespace osu.Game.Overlays { c.Anchor = Anchor.TopCentre; c.Origin = Anchor.TopCentre; + c.Scale = new Vector2(0.8f); })).ToArray(); private static ReverseChildIDFillFlowContainer createCardContainerFor(IEnumerable newCards) From 3249ecee2731f49f95d8095e4bb96371d301e4a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 12:31:19 +0800 Subject: [PATCH 363/581] Fix chat overlay being far too large --- osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs | 4 ++-- osu.Game/Overlays/Chat/ChatLine.cs | 2 +- osu.Game/Overlays/Chat/ChatTextBar.cs | 9 ++++++--- osu.Game/Overlays/ChatOverlay.cs | 3 +-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 87b1f4ef01..e8c251e7fd 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Chat.ChannelList [BackgroundDependencyLoader] private void load() { - Height = 30; + Height = 25; RelativeSizeAxes = Axes.X; Children = new Drawable[] @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Chat.ChannelList Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Text = Channel.Name, - Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold), + Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Colour = colourProvider.Light3, Margin = new MarginPadding { Bottom = 2 }, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index bbc3ee5bf4..9bcca3ac9d 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Chat public IReadOnlyCollection DrawableContentFlow => drawableContentFlow; - protected virtual float FontSize => 20; + protected virtual float FontSize => 14; protected virtual float Spacing => 15; diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs index 16a8d14b10..0a42363279 100644 --- a/osu.Game/Overlays/Chat/ChatTextBar.cs +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -19,6 +19,8 @@ namespace osu.Game.Overlays.Chat { public partial class ChatTextBar : Container { + public const float HEIGHT = 40; + public readonly BindableBool ShowSearch = new BindableBool(); public event Action? OnChatMessageCommitted; @@ -45,7 +47,7 @@ namespace osu.Game.Overlays.Chat private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; - Height = 60; + Height = HEIGHT; Children = new Drawable[] { @@ -76,7 +78,7 @@ namespace osu.Game.Overlays.Chat Child = chattingText = new TruncatingSpriteText { MaxWidth = chatting_text_width - padding * 2, - Font = OsuFont.Torus.With(size: 20), + Font = OsuFont.Torus, Colour = colourProvider.Background1, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -91,7 +93,7 @@ namespace osu.Game.Overlays.Chat Icon = FontAwesome.Solid.Search, Origin = Anchor.CentreRight, Anchor = Anchor.CentreRight, - Size = new Vector2(20), + Size = new Vector2(OsuFont.DEFAULT_FONT_SIZE), Margin = new MarginPadding { Right = 2 }, }, }, @@ -101,6 +103,7 @@ namespace osu.Game.Overlays.Chat Padding = new MarginPadding { Right = padding }, Child = chatTextBox = new ChatTextBox { + FontSize = OsuFont.DEFAULT_FONT_SIZE, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8f3b7031c2..b11483e678 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -55,7 +55,6 @@ namespace osu.Game.Overlays private const int transition_length = 500; private const float top_bar_height = 40; private const float side_bar_width = 190; - private const float chat_bar_height = 60; protected override string PopInSampleName => @"UI/overlay-big-pop-in"; protected override string PopOutSampleName => @"UI/overlay-big-pop-out"; @@ -136,7 +135,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Left = side_bar_width, - Bottom = chat_bar_height, + Bottom = ChatTextBar.HEIGHT, }, Children = new Drawable[] { From 058bd5ced66d8d6937f77d41f3e4704089177530 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 13:38:27 +0800 Subject: [PATCH 364/581] Stop using visually noisy `bg4` for default backgrounds This has always really annoyed me. --- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index d254945a51..35067f4055 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -53,7 +53,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => new Beatmap(); - public override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4"); + public override Texture GetBackground() => textures?.Get(@"Backgrounds/bg2"); protected override Track GetBeatmapTrack() => GetVirtualTrack(); diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index ab99370603..7e4f4f4e9b 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -422,7 +422,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load(LargeTextureStore textures) { - sprite.Texture = beatmap.GetBackground() ?? textures.Get(@"Backgrounds/bg4"); + sprite.Texture = beatmap.GetBackground() ?? textures.Get(@"Backgrounds/bg2"); } } From c1e9b6d4cae0793de9bc15cc055808db4995b1a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 13:42:56 +0800 Subject: [PATCH 365/581] Fix beatmap backgrounds loading default briefly before final display Due to the way `ModelBackedDrawable` works, the default starts to get loaded even though a final `Beatmap` has been set. This avoids loading the default fallback unless a beatmap has been set (and has no background itself). --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 0bb60847e5..f067af5360 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -52,6 +52,9 @@ namespace osu.Game.Beatmaps.Drawables private Drawable getDrawableForModel(IBeatmapInfo? model) { + if (model == null) + return Empty(); + // prefer online cover where available. if (model?.BeatmapSet is IBeatmapSetOnlineInfo online) return new OnlineBeatmapSetCover(online, beatmapSetCoverType); From c21b7c7df994fa6a00a3edf7bc79653035bbaa8c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 2 May 2024 22:46:42 -0700 Subject: [PATCH 366/581] Use `IsLoaded` instead of `Schedule` --- osu.Game/Overlays/NowPlayingOverlay.cs | 35 ++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 8f97ce50cc..744d32ff7e 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -476,29 +476,35 @@ namespace osu.Game.Overlays private const float initial_move_delay = 1000; private const float pixels_per_second = 50; - private LocalisableString text; private OsuSpriteText mainSpriteText = null!; private OsuSpriteText fillerSpriteText = null!; + private LocalisableString text; + public LocalisableString Text { get => text; set { text = value; - Schedule(updateText); + + if (IsLoaded) + updateText(); } } + private FontUsage font = OsuFont.Default; + public FontUsage Font { - set => Schedule(() => + get => font; + set { - mainSpriteText.Font = value; - fillerSpriteText.Font = value; + font = value; - updateText(); - }); + if (IsLoaded) + updateFontAndText(); + } } public ScrollingTextContainer() @@ -521,6 +527,21 @@ namespace osu.Game.Overlays }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + updateFontAndText(); + } + + private void updateFontAndText() + { + mainSpriteText.Font = font; + fillerSpriteText.Font = font; + + updateText(); + } + private void updateText() { mainSpriteText.Text = text; From c935d3bf6c12e0b6cd5e9213dcd9f3d68c27b22b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 14:00:28 +0800 Subject: [PATCH 367/581] Reduce font size in channel listing too --- osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs index 9c85c73ee4..466f8b2f5d 100644 --- a/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs +++ b/osu.Game/Overlays/Chat/Listing/ChannelListingItem.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Chat.Listing [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - private const float text_size = 18; + private const float text_size = 14; private const float icon_size = 14; private const float vertical_margin = 1.5f; From d1a50ff85bbfb4a502bd4df25a53d93386d7f920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 08:11:00 +0200 Subject: [PATCH 368/581] Remove redundant conditional access --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index f067af5360..6f71fa90b8 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -56,7 +56,7 @@ namespace osu.Game.Beatmaps.Drawables return Empty(); // prefer online cover where available. - if (model?.BeatmapSet is IBeatmapSetOnlineInfo online) + if (model.BeatmapSet is IBeatmapSetOnlineInfo online) return new OnlineBeatmapSetCover(online, beatmapSetCoverType); if (model is BeatmapInfo localModel) From 42e49067e55ddb232d6fe1af0af44234a772f5fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 17:10:59 +0800 Subject: [PATCH 369/581] Move `Room.Status` updates to a common location --- osu.Game/Online/Rooms/GetRoomsRequest.cs | 19 +++++++++++++++++++ osu.Game/Online/Rooms/Room.cs | 6 ++---- .../Lounge/Components/RoomStatusPill.cs | 12 +----------- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 +++ 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 7feb709acb..bfb2629c64 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using osu.Framework.IO.Network; using osu.Game.Extensions; using osu.Game.Online.API; +using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Screens.OnlinePlay.Lounge.Components; namespace osu.Game.Online.Rooms @@ -33,6 +35,23 @@ namespace osu.Game.Online.Rooms return req; } + protected override void PostProcess() + { + base.PostProcess(); + + if (Response != null) + { + // API doesn't populate status so let's do it here. + foreach (var room in Response) + { + if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value) + room.Status.Value = new RoomStatusEnded(); + else + room.Status.Value = new RoomStatusOpen(); + } + } + } + protected override string Target => "rooms"; } } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 8f346c4057..23c77f8773 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -111,8 +111,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); + [Cached] [JsonProperty("has_password")] - public readonly BindableBool HasPassword = new BindableBool(); + public readonly Bindable HasPassword = new Bindable(); [Cached] [JsonProperty("recent_participants")] @@ -201,9 +202,6 @@ namespace osu.Game.Online.Rooms CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value; AutoSkip.Value = other.AutoSkip.Value; - if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value) - Status.Value = new RoomStatusEnded(); - other.RemoveExpiredPlaylistItems(); if (!Playlist.SequenceEqual(other.Playlist)) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index aae82b6721..96d698a184 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -1,13 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Online.Rooms; -using osu.Game.Online.Rooms.RoomStatuses; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { @@ -36,18 +34,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateDisplay() { - RoomStatus status = getDisplayStatus(); + RoomStatus status = Status.Value; Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); TextFlow.Text = status.Message; } - - private RoomStatus getDisplayStatus() - { - if (EndDate.Value < DateTimeOffset.Now) - return new RoomStatusEnded(); - - return Status.Value; - } } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index ff536a65c4..83df1c6161 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -77,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] public Bindable Password { get; private set; } + [Resolved(typeof(Room))] + public Bindable HasPassword { get; private set; } + [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } From 7141177966c82ffa6f9e947687422f45645c4966 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 May 2024 17:11:29 +0800 Subject: [PATCH 370/581] Better signify private rooms by showing a different status pill design --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/GetRoomsRequest.cs | 2 ++ .../Rooms/RoomStatuses/RoomStatusOpenPrivate.cs | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpenPrivate.cs diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bbf0e3697a..871fbc15b3 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -396,7 +396,7 @@ namespace osu.Game.Online.Multiplayer switch (state) { case MultiplayerRoomState.Open: - APIRoom.Status.Value = new RoomStatusOpen(); + APIRoom.Status.Value = APIRoom.HasPassword.Value ? new RoomStatusOpenPrivate() : new RoomStatusOpen(); break; case MultiplayerRoomState.Playing: diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index bfb2629c64..1b5e08c729 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -46,6 +46,8 @@ namespace osu.Game.Online.Rooms { if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value) room.Status.Value = new RoomStatusEnded(); + else if (room.HasPassword.Value) + room.Status.Value = new RoomStatusOpenPrivate(); else room.Status.Value = new RoomStatusOpen(); } diff --git a/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpenPrivate.cs b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpenPrivate.cs new file mode 100644 index 0000000000..d71e706c76 --- /dev/null +++ b/osu.Game/Online/Rooms/RoomStatuses/RoomStatusOpenPrivate.cs @@ -0,0 +1,14 @@ +// 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.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Online.Rooms.RoomStatuses +{ + public class RoomStatusOpenPrivate : RoomStatus + { + public override string Message => "Open (Private)"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDark; + } +} From f57818f5a2245d2b2b27a19555fc65f7e5575ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 11:25:50 +0200 Subject: [PATCH 371/581] Add visual test coverage of private room status --- osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 4ffccdbf0e..98242e2d92 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -69,8 +69,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }), createLoungeRoom(new Room { - Name = { Value = "Multiplayer room" }, - Status = { Value = new RoomStatusOpen() }, + Name = { Value = "Private room" }, + Status = { Value = new RoomStatusOpenPrivate() }, + HasPassword = { Value = true }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = { Value = MatchType.HeadToHead }, Playlist = From 221b4cd599df85d23212519a8a68bec8bb1e7797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 11:36:18 +0200 Subject: [PATCH 372/581] Remove unused cache --- osu.Game/Online/Rooms/Room.cs | 1 - osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 3 --- 2 files changed, 4 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 23c77f8773..5abf5034d9 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -111,7 +111,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); - [Cached] [JsonProperty("has_password")] public readonly Bindable HasPassword = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 83df1c6161..ff536a65c4 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -77,9 +77,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] public Bindable Password { get; private set; } - [Resolved(typeof(Room))] - public Bindable HasPassword { get; private set; } - [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } From 7d31af6f16f3ecbc6d04051bad0a2b5284cb9326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 11:34:42 +0200 Subject: [PATCH 373/581] Fix room status not updating when password is changed while inside the room --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 871fbc15b3..77ede1fd35 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -816,6 +816,7 @@ namespace osu.Game.Online.Multiplayer Room.Settings = settings; APIRoom.Name.Value = Room.Settings.Name; APIRoom.Password.Value = Room.Settings.Password; + APIRoom.Status.Value = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate(); APIRoom.Type.Value = Room.Settings.MatchType; APIRoom.QueueMode.Value = Room.Settings.QueueMode; APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; From 1b7652e60d37d496c9c1f01a984e22b743904d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 13:47:10 +0200 Subject: [PATCH 374/581] Add failing tests --- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 192 ++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index ebbc329b9d..eb2c098ab8 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -15,6 +15,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; @@ -23,6 +24,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Resources; +using osu.Game.Users; namespace osu.Game.Tests.Scores.IO { @@ -284,6 +286,196 @@ namespace osu.Game.Tests.Scores.IO } } + [Test] + public void TestUserLookedUpForOnlineScore() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + var api = (DummyAPIAccess)osu.API; + api.HandleRequest = req => + { + switch (req) + { + case GetUserRequest userRequest: + userRequest.TriggerSuccess(new APIUser + { + Username = "Test user", + CountryCode = CountryCode.JP, + Id = 1234 + }); + return true; + + default: + return false; + } + }; + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser { Username = "Test user" }, + Date = DateTimeOffset.Now, + OnlineID = 12345, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmap.Beatmaps.First() + }; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.User.Username, imported.User.Username); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + Assert.AreEqual(toImport.User.Username, imported.RealmUser.Username); + Assert.AreEqual(1234, imported.RealmUser.OnlineID); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestUserLookedUpForLegacyOnlineScore() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + var api = (DummyAPIAccess)osu.API; + api.HandleRequest = req => + { + switch (req) + { + case GetUserRequest userRequest: + userRequest.TriggerSuccess(new APIUser + { + Username = "Test user", + CountryCode = CountryCode.JP, + Id = 1234 + }); + return true; + + default: + return false; + } + }; + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser { Username = "Test user" }, + Date = DateTimeOffset.Now, + LegacyOnlineID = 12345, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmap.Beatmaps.First() + }; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.User.Username, imported.User.Username); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + Assert.AreEqual(toImport.User.Username, imported.RealmUser.Username); + Assert.AreEqual(1234, imported.RealmUser.OnlineID); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestUserNotLookedUpForOfflineScore() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + var api = (DummyAPIAccess)osu.API; + api.HandleRequest = req => + { + switch (req) + { + case GetUserRequest userRequest: + userRequest.TriggerSuccess(new APIUser + { + Username = "Test user", + CountryCode = CountryCode.JP, + Id = 1234 + }); + return true; + + default: + return false; + } + }; + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser { Username = "Test user" }, + Date = DateTimeOffset.Now, + OnlineID = -1, + LegacyOnlineID = -1, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmap.Beatmaps.First() + }; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.User.Username, imported.User.Username); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + Assert.AreEqual(toImport.User.Username, imported.RealmUser.Username); + Assert.That(imported.RealmUser.OnlineID, Is.LessThanOrEqualTo(1)); + } + finally + { + host.Exit(); + } + } + } + public static ScoreInfo LoadScoreIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null) { // clone to avoid attaching the input score to realm. From afb491dff064967f4ccc6fe18f26e94e9322c570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 13:48:06 +0200 Subject: [PATCH 375/581] Do not perform username lookups for scores without an online ID --- osu.Game/Scoring/ScoreImporter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 768c28cc38..4ae8e51f6d 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -127,6 +127,9 @@ namespace osu.Game.Scoring if (model.RealmUser.OnlineID == APIUser.SYSTEM_USER_ID) return; + if (model.OnlineID < 0 && model.LegacyOnlineID <= 0) + return; + string username = model.RealmUser.Username; if (usernameLookupCache.TryGetValue(username, out var existing)) From a23d25e0a15c5ac6f3ab8565d3e4b2733501ff81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 14:27:34 +0200 Subject: [PATCH 376/581] Fix `BeatmapAttributeText` breaking due to enum serialisation woes --- osu.Game/Skinning/Components/BeatmapAttributeText.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Components/BeatmapAttributeText.cs b/osu.Game/Skinning/Components/BeatmapAttributeText.cs index 5c5e509fb2..c467b2e946 100644 --- a/osu.Game/Skinning/Components/BeatmapAttributeText.cs +++ b/osu.Game/Skinning/Components/BeatmapAttributeText.cs @@ -125,6 +125,8 @@ namespace osu.Game.Skinning.Components protected override void SetFont(FontUsage font) => text.Font = font.With(size: 40); } + // WARNING: DO NOT ADD ANY VALUES TO THIS ENUM ANYWHERE ELSE THAN AT THE END. + // Doing so will break existing user skins. public enum BeatmapAttribute { CircleSize, @@ -134,11 +136,11 @@ namespace osu.Game.Skinning.Components StarRating, Title, Artist, - Source, DifficultyName, Creator, Length, RankedStatus, BPM, + Source, } } From e0e7e123bf7d052b074d1cbfbda82e6678a5d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Brandst=C3=B6tter?= Date: Fri, 3 May 2024 16:57:31 +0200 Subject: [PATCH 377/581] Keep menus open when clicking a stateful item with CTRL held --- .../UserInterface/DrawableStatefulMenuItem.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs index 5af275c9e7..d63aaf2053 100644 --- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -4,6 +4,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; using osuTK; namespace osu.Game.Graphics.UserInterface @@ -19,6 +20,17 @@ namespace osu.Game.Graphics.UserInterface protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item); + private InputManager inputManager = null!; + + public override bool CloseMenuOnClick => !inputManager.CurrentState.Keyboard.ControlPressed; + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + private partial class ToggleTextContainer : TextContainer { private readonly StatefulMenuItem menuItem; From 0b61e2cd421d2bd8468fc836c23d93dfd07d429c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Apr 2024 15:52:36 +0800 Subject: [PATCH 378/581] Use closest origin along with closest anchor --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- .../SkinEditor/SkinSelectionHandler.cs | 35 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 690c6b35e3..5bf28ae79b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -457,7 +457,7 @@ namespace osu.Game.Overlays.SkinEditor } SelectedComponents.Add(component); - SkinSelectionHandler.ApplyClosestAnchor(drawableComponent); + SkinSelectionHandler.ApplyClosestAnchorOrigin(drawableComponent); return true; } diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index cf6fb60636..f41bad4716 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -204,15 +204,20 @@ namespace osu.Game.Overlays.SkinEditor drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); - if (item.UsesFixedAnchor) continue; - - ApplyClosestAnchor(drawable); + if (!item.UsesFixedAnchor) + ApplyClosestAnchorOrigin(drawable); } return true; } - public static void ApplyClosestAnchor(Drawable drawable) => applyAnchor(drawable, getClosestAnchor(drawable)); + public static void ApplyClosestAnchorOrigin(Drawable drawable) + { + var closest = getClosestAnchor(drawable); + + applyAnchor(drawable, closest); + applyOrigin(drawable, closest); + } protected override void OnSelectionChanged() { @@ -325,15 +330,10 @@ namespace osu.Game.Overlays.SkinEditor { var drawable = (Drawable)item; - if (origin == drawable.Origin) continue; + applyOrigin(drawable, origin); - var previousOrigin = drawable.OriginPosition; - drawable.Origin = origin; - drawable.Position += drawable.OriginPosition - previousOrigin; - - if (item.UsesFixedAnchor) continue; - - ApplyClosestAnchor(drawable); + if (item.UsesFixedAnchor) + ApplyClosestAnchorOrigin(drawable); } OnOperationEnded(); @@ -368,7 +368,7 @@ namespace osu.Game.Overlays.SkinEditor foreach (var item in SelectedItems) { item.UsesFixedAnchor = false; - ApplyClosestAnchor((Drawable)item); + ApplyClosestAnchorOrigin((Drawable)item); } OnOperationEnded(); @@ -414,6 +414,15 @@ namespace osu.Game.Overlays.SkinEditor drawable.Position -= drawable.AnchorPosition - previousAnchor; } + private static void applyOrigin(Drawable drawable, Anchor origin) + { + if (origin == drawable.Origin) return; + + var previousOrigin = drawable.OriginPosition; + drawable.Origin = origin; + drawable.Position += drawable.OriginPosition - previousOrigin; + } + private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) { // cancel out scale in axes we don't care about (based on which drag handle was used). From e7ca02ffde25da718116566947f8d7b09e7ba961 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 May 2024 13:28:33 +0800 Subject: [PATCH 379/581] Fix position changing when origin updates during a drag --- osu.Game/Overlays/SkinEditor/SkinBlueprint.cs | 4 +++- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs index 8f8d899fad..6b59d940cc 100644 --- a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs +++ b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs @@ -40,7 +40,9 @@ namespace osu.Game.Overlays.SkinEditor public override bool Contains(Vector2 screenSpacePos) => drawableQuad.Contains(screenSpacePos); - public override Vector2 ScreenSpaceSelectionPoint => drawable.ToScreenSpace(drawable.OriginPosition); + public override Vector2 ScreenSpaceSelectionPoint => + // Important to use a stable position (not based on origin) as origin may be automatically updated during drag operations. + drawable.ScreenSpaceDrawQuad.Centre; protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => drawableQuad.Contains(screenSpacePos); diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index f41bad4716..680cc02311 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -202,10 +202,10 @@ namespace osu.Game.Overlays.SkinEditor var item = c.Item; Drawable drawable = (Drawable)item; - drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); - if (!item.UsesFixedAnchor) ApplyClosestAnchorOrigin(drawable); + + drawable.Position += drawable.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta); } return true; @@ -332,7 +332,7 @@ namespace osu.Game.Overlays.SkinEditor applyOrigin(drawable, origin); - if (item.UsesFixedAnchor) + if (!item.UsesFixedAnchor) ApplyClosestAnchorOrigin(drawable); } From 2cb367fdce2a04181bb71e0c2545e07ae975f6e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 4 May 2024 13:29:05 +0800 Subject: [PATCH 380/581] Disable "origin" menu when in "Closest" placement mode --- .../Overlays/SkinEditor/SkinSelectionHandler.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 680cc02311..8b9e6436b1 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -23,6 +23,8 @@ namespace osu.Game.Overlays.SkinEditor { public partial class SkinSelectionHandler : SelectionHandler { + private OsuMenuItem originMenu = null!; + [Resolved] private SkinEditor skinEditor { get; set; } = null!; @@ -248,10 +250,15 @@ namespace osu.Game.Overlays.SkinEditor .ToArray() }; - yield return new OsuMenuItem("Origin") + yield return originMenu = new OsuMenuItem("Origin"); + + closestItem.State.BindValueChanged(s => { - Items = createAnchorItems((d, o) => ((Drawable)d).Origin == o, applyOrigins).ToArray() - }; + // For UX simplicity, origin should only be user-editable when "closest" anchor mode is disabled. + originMenu.Items = s.NewValue == TernaryState.True + ? Array.Empty() + : createAnchorItems((d, o) => ((Drawable)d).Origin == o, applyOrigins).ToArray(); + }, true); yield return new OsuMenuItemSpacer(); From b35f2c99e64e67b21fea9c272de843c9aa41c51d Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 4 May 2024 18:43:04 +0800 Subject: [PATCH 381/581] add failed test --- .../UserInterface/TestSceneModPresetPanel.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index c79cbd3691..d0303b3849 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -123,6 +123,34 @@ namespace osu.Game.Tests.Visual.UserInterface assertSelectedModsEquivalentTo(new Mod[] { new OsuModTouchDevice(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } }); } + [Test] + public void TestActivatingPresetWithAutoplayWhenSystemModEnabled() + { + ModPresetPanel? panel = null; + + AddStep("create panel", () => Child = panel = new ModPresetPanel(new ModPreset + { + Name = "Autoplay include", + Description = "no way", + Mods = new Mod[] + { + new OsuModAutoplay() + }, + Ruleset = new OsuRuleset().RulesetInfo + }.ToLiveUnmanaged()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f + }); + + AddStep("Add touch device to selected mod", () => SelectedMods.Value = new Mod[] { new OsuModTouchDevice() }); + AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); + + // touch device should be removed due to incompatible with autoplay. + assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutoplay() }); + } + private void assertSelectedModsEquivalentTo(IEnumerable mods) => AddAssert("selected mods changed correctly", () => new HashSet(SelectedMods.Value).SetEquals(mods)); From f9be9ed479555a355be8bc3673540ce312fbf39e Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 4 May 2024 18:44:47 +0800 Subject: [PATCH 382/581] remove incompatible system mods before enable preset --- osu.Game/Overlays/Mods/ModPresetPanel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index 3982abeba7..ca7e64957f 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.cs @@ -55,7 +55,8 @@ namespace osu.Game.Overlays.Mods protected override void Select() { - var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System); + var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System + && !mod.IncompatibleMods.Any(t => Preset.Value.Mods.Any(m => m.GetType() == t))); // will also have the side effect of activating the preset (see `updateActiveState()`). selectedMods.Value = Preset.Value.Mods.Concat(selectedSystemMods).ToArray(); } From 6af30a3d45ef7d4a971082be060407778c148a4b Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 4 May 2024 20:02:35 +0800 Subject: [PATCH 383/581] add test for non-td system mod --- .../Visual/UserInterface/TestSceneModPresetPanel.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index d0303b3849..9a141e0df0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -149,6 +149,15 @@ namespace osu.Game.Tests.Visual.UserInterface // touch device should be removed due to incompatible with autoplay. assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutoplay() }); + + AddStep("deactivate panel", () => panel.AsNonNull().TriggerClick()); + assertSelectedModsEquivalentTo(Array.Empty()); + + // just for test purpose + AddStep("Add score v2 to selected mod", () => SelectedMods.Value = new Mod[] { new ModScoreV2() }); + AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); + + assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutoplay(), new ModScoreV2() }); } private void assertSelectedModsEquivalentTo(IEnumerable mods) From fe30ca3d397b774eb8ab8eafe576a5d42b383b83 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 4 May 2024 20:11:59 +0800 Subject: [PATCH 384/581] fix linq logic --- osu.Game/Overlays/Mods/ModPresetPanel.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index ca7e64957f..9f5dda4a75 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.cs @@ -55,8 +55,10 @@ namespace osu.Game.Overlays.Mods protected override void Select() { - var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System - && !mod.IncompatibleMods.Any(t => Preset.Value.Mods.Any(m => m.GetType() == t))); + var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System && + !mod.IncompatibleMods.Any(t => Preset.Value.Mods.Any(t.IsInstanceOfType))); + + // will also have the side effect of activating the preset (see `updateActiveState()`). selectedMods.Value = Preset.Value.Mods.Concat(selectedSystemMods).ToArray(); } From 1f92f1d19b15a652e580f0531b0a70f7a5788c94 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 4 May 2024 20:41:36 +0800 Subject: [PATCH 385/581] remove blank line nt --- osu.Game/Overlays/Mods/ModPresetPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index 9f5dda4a75..450c684e54 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.cs @@ -58,7 +58,6 @@ namespace osu.Game.Overlays.Mods var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System && !mod.IncompatibleMods.Any(t => Preset.Value.Mods.Any(t.IsInstanceOfType))); - // will also have the side effect of activating the preset (see `updateActiveState()`). selectedMods.Value = Preset.Value.Mods.Concat(selectedSystemMods).ToArray(); } From 21917218ce17a2f3422b1e34e4d2ea42de0cb52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Brandst=C3=B6tter?= Date: Sat, 4 May 2024 16:00:22 +0200 Subject: [PATCH 386/581] No longer keep menu open when CTRL is held --- .../Graphics/UserInterface/DrawableStatefulMenuItem.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs index d63aaf2053..000a2f9f91 100644 --- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -2,9 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; +using osu.Framework.Graphics; using osuTK; namespace osu.Game.Graphics.UserInterface @@ -20,15 +19,8 @@ namespace osu.Game.Graphics.UserInterface protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item); - private InputManager inputManager = null!; - - public override bool CloseMenuOnClick => !inputManager.CurrentState.Keyboard.ControlPressed; - - protected override void LoadComplete() { - base.LoadComplete(); - inputManager = GetContainingInputManager(); } private partial class ToggleTextContainer : TextContainer From c62952ea3afd2d81ba3f703491525c5249b79cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Brandst=C3=B6tter?= Date: Sat, 4 May 2024 16:01:31 +0200 Subject: [PATCH 387/581] Invoke the registered Action when a stateful item is right clicked --- .../Graphics/UserInterface/DrawableStatefulMenuItem.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs index 000a2f9f91..0c4e575621 100644 --- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -4,6 +4,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osuTK.Input; using osuTK; namespace osu.Game.Graphics.UserInterface @@ -19,8 +21,16 @@ namespace osu.Game.Graphics.UserInterface protected override TextContainer CreateTextContainer() => new ToggleTextContainer(Item); + protected override bool OnMouseDown(MouseDownEvent e) { + if (!IsActionable) + return true; + if (e.Button != MouseButton.Right) + return true; + + Item.Action.Value?.Invoke(); + return true; } private partial class ToggleTextContainer : TextContainer From 78d6f24fcae2e11c1c5fe7ba148fe4dd15f4a3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Brandst=C3=B6tter?= Date: Sat, 4 May 2024 16:25:19 +0200 Subject: [PATCH 388/581] Add Test --- .../TestSceneStatefulMenuItem.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs index 88187f1808..63497040db 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStatefulMenuItem.cs @@ -114,6 +114,51 @@ namespace osu.Game.Tests.Visual.UserInterface => AddAssert($"state is {expected}", () => state.Value == expected); } + [Test] + public void TestItemRespondsToRightClick() + { + OsuMenu menu = null; + + Bindable state = new Bindable(TernaryState.Indeterminate); + + AddStep("create menu", () => + { + state.Value = TernaryState.Indeterminate; + + Child = menu = new OsuMenu(Direction.Vertical, true) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Items = new[] + { + new TernaryStateToggleMenuItem("First"), + new TernaryStateToggleMenuItem("Second") { State = { BindTarget = state } }, + new TernaryStateToggleMenuItem("Third") { State = { Value = TernaryState.True } }, + } + }; + }); + + checkState(TernaryState.Indeterminate); + + click(); + checkState(TernaryState.True); + + click(); + checkState(TernaryState.False); + + AddStep("change state via bindable", () => state.Value = TernaryState.True); + + void click() => + AddStep("click", () => + { + InputManager.MoveMouseTo(menu.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Right); + }); + + void checkState(TernaryState expected) + => AddAssert($"state is {expected}", () => state.Value == expected); + } + [Test] public void TestCustomState() { From b1696db9c87d59fd047aa3b94c2b34577e68c593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Brandst=C3=B6tter?= Date: Sat, 4 May 2024 16:32:18 +0200 Subject: [PATCH 389/581] Reorder imports with `dotnet format` --- osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs index 0c4e575621..8ed52593a7 100644 --- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osuTK.Input; using osuTK; +using osuTK.Input; namespace osu.Game.Graphics.UserInterface { From cf313cd67f3c1a89b77edd90c94a73a5794e31d7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 May 2024 21:53:48 +0300 Subject: [PATCH 390/581] Use single path to display slider control point connections --- .../TestScenePathControlPointVisualiser.cs | 35 ---------------- ...Piece.cs => PathControlPointConnection.cs} | 42 +++++-------------- .../Components/PathControlPointVisualiser.cs | 29 +------------ 3 files changed, 11 insertions(+), 95 deletions(-) rename osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/{PathControlPointConnectionPiece.cs => PathControlPointConnection.cs} (51%) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 0ca30e00bc..9af028fd8c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -30,23 +30,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); }); - [Test] - public void TestAddOverlappingControlPoints() - { - createVisualiser(true); - - addControlPointStep(new Vector2(200)); - addControlPointStep(new Vector2(300)); - addControlPointStep(new Vector2(300)); - addControlPointStep(new Vector2(500, 300)); - - AddAssert("last connection displayed", () => - { - var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position == new Vector2(300)); - return lastConnection.DrawWidth > 50; - }); - } - [Test] public void TestPerfectCurveTooManyPoints() { @@ -194,24 +177,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addAssertPointPositionChanged(points, i); } - [Test] - public void TestStackingUpdatesConnectionPosition() - { - createVisualiser(true); - - Vector2 connectionPosition; - addControlPointStep(connectionPosition = new Vector2(300)); - addControlPointStep(new Vector2(600)); - - // Apply a big number in stacking so the person running the test can clearly see if it fails - AddStep("apply stacking", () => slider.StackHeightBindable.Value += 10); - - AddAssert($"Connection at {connectionPosition} changed", - () => visualiser.Connections[0].Position, - () => !Is.EqualTo(connectionPosition) - ); - } - private void addAssertPointPositionChanged(Vector2[] points, int index) { AddAssert($"Point at {points.ElementAt(index)} changed", diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs similarity index 51% rename from osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs rename to osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs index 9b3d8fc7a7..5706ed4baf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs @@ -4,10 +4,7 @@ #nullable disable using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -15,36 +12,21 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { /// - /// A visualisation of the line between two s. + /// A visualisation of the lines between s. /// - /// The type of which this visualises. - public partial class PathControlPointConnectionPiece : CompositeDrawable where T : OsuHitObject, IHasPath + /// The type of which this visualises. + public partial class PathControlPointConnection : SmoothPath where T : OsuHitObject, IHasPath { - public readonly PathControlPoint ControlPoint; - - private readonly Path path; private readonly T hitObject; - public int ControlPointIndex { get; set; } private IBindable hitObjectPosition; private IBindable pathVersion; private IBindable stackHeight; - public PathControlPointConnectionPiece(T hitObject, int controlPointIndex) + public PathControlPointConnection(T hitObject) { this.hitObject = hitObject; - ControlPointIndex = controlPointIndex; - - Origin = Anchor.Centre; - AutoSizeAxes = Axes.Both; - - ControlPoint = hitObject.Path.ControlPoints[controlPointIndex]; - - InternalChild = path = new SmoothPath - { - Anchor = Anchor.Centre, - PathRadius = 1 - }; + PathRadius = 1; } protected override void LoadComplete() @@ -68,18 +50,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updateConnectingPath() { - Position = hitObject.StackedPosition + ControlPoint.Position; + Position = hitObject.StackedPosition; - path.ClearVertices(); + ClearVertices(); - int nextIndex = ControlPointIndex + 1; - if (nextIndex == 0 || nextIndex >= hitObject.Path.ControlPoints.Count) - return; + foreach (var controlPoint in hitObject.Path.ControlPoints) + AddVertex(controlPoint.Position); - path.AddVertex(Vector2.Zero); - path.AddVertex(hitObject.Path.ControlPoints[nextIndex].Position - ControlPoint.Position); - - path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); + OriginPosition = PositionInBoundingBox(Vector2.Zero); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index b2d1709531..7212de322d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -37,7 +37,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // allow context menu to appear outside of the playfield. internal readonly Container> Pieces; - internal readonly Container> Connections; private readonly IBindableList controlPoints = new BindableList(); private readonly T hitObject; @@ -63,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChildren = new Drawable[] { - Connections = new Container> { RelativeSizeAxes = Axes.Both }, + new PathControlPointConnection(hitObject), Pieces = new Container> { RelativeSizeAxes = Axes.Both } }; } @@ -185,17 +184,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components case NotifyCollectionChangedAction.Add: Debug.Assert(e.NewItems != null); - // If inserting in the path (not appending), - // update indices of existing connections after insert location - if (e.NewStartingIndex < Pieces.Count) - { - foreach (var connection in Connections) - { - if (connection.ControlPointIndex >= e.NewStartingIndex) - connection.ControlPointIndex += e.NewItems.Count; - } - } - for (int i = 0; i < e.NewItems.Count; i++) { var point = (PathControlPoint)e.NewItems[i]; @@ -209,8 +197,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components d.DragInProgress = DragInProgress; d.DragEnded = DragEnded; })); - - Connections.Add(new PathControlPointConnectionPiece(hitObject, e.NewStartingIndex + i)); } break; @@ -222,19 +208,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray()) piece.RemoveAndDisposeImmediately(); - foreach (var connection in Connections.Where(c => c.ControlPoint == point).ToArray()) - connection.RemoveAndDisposeImmediately(); - } - - // If removing before the end of the path, - // update indices of connections after remove location - if (e.OldStartingIndex < Pieces.Count) - { - foreach (var connection in Connections) - { - if (connection.ControlPointIndex >= e.OldStartingIndex) - connection.ControlPointIndex -= e.OldItems.Count; - } } break; From e319a3e885e9ae7d545c70a274ecad87220f62c7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 4 May 2024 22:07:08 +0300 Subject: [PATCH 391/581] Don't perform masking updates in PathControlPointVisualiser --- .../Sliders/Components/PathControlPointVisualiser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 7212de322d..836d348ff4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -77,6 +77,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components controlPoints.BindTo(hitObject.Path.ControlPoints); } + // Generally all the control points are within the visible area all the time. + public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => true; + /// /// Handles correction of invalid path types. /// From 9e7712740b2d881c14bb8f0872e124d95436fe0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 5 May 2024 23:32:24 +0800 Subject: [PATCH 392/581] Refactor for legibility --- .../Graphics/UserInterface/DrawableStatefulMenuItem.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs index 8ed52593a7..686c490930 100644 --- a/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableStatefulMenuItem.cs @@ -23,13 +23,11 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(MouseDownEvent e) { - if (!IsActionable) - return true; + // Right mouse button is a special case where we allow actioning without dismissing the menu. + // This is achieved by not calling `Clicked` (as done by the base implementation in OnClick). + if (IsActionable && e.Button == MouseButton.Right) + Item.Action.Value?.Invoke(); - if (e.Button != MouseButton.Right) - return true; - - Item.Action.Value?.Invoke(); return true; } From 848e497c94d1d0a440cc74b05dfa441f19dfb40b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 5 May 2024 21:32:25 +0300 Subject: [PATCH 393/581] Add "HP" to the abbreviations list --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 51af281ac6..08eb264aab 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -347,6 +347,7 @@ GL GLSL HID + HP HSL HSPA HSV From 1665c5e0e1e3e3434cd98c0f0075362b0e7ea5a5 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 5 May 2024 14:41:48 -0700 Subject: [PATCH 394/581] Use existing `AutomaticallyDownloadMissingBeatmaps` localisation on solo spectator screen --- osu.Game/Screens/Play/SoloSpectatorScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SoloSpectatorScreen.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs index 2db751402c..95eb2d4376 100644 --- a/osu.Game/Screens/Play/SoloSpectatorScreen.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -16,6 +16,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -138,7 +139,7 @@ namespace osu.Game.Screens.Play }, automaticDownload = new SettingsCheckbox { - LabelText = "Automatically download beatmaps", + LabelText = OnlineSettingsStrings.AutomaticallyDownloadMissingBeatmaps, Current = config.GetBindable(OsuSetting.AutomaticallyDownloadMissingBeatmaps), Anchor = Anchor.Centre, Origin = Anchor.Centre, From eb92e8de370d5fff1407c64527b3760d6b63ac82 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 5 May 2024 15:15:14 -0700 Subject: [PATCH 395/581] Edit title/artist unicode values and add unicode toggle in test --- .../UserInterface/TestSceneNowPlayingOverlay.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index 1670741cbd..d84089fb6f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Overlays; @@ -21,8 +22,10 @@ namespace osu.Game.Tests.Visual.UserInterface private NowPlayingOverlay nowPlayingOverlay; [BackgroundDependencyLoader] - private void load() + private void load(FrameworkConfigManager frameworkConfig) { + AddToggleStep("toggle unicode", v => frameworkConfig.SetValue(FrameworkSetting.ShowUnicode, v)); + nowPlayingOverlay = new NowPlayingOverlay { Origin = Anchor.Centre, @@ -50,9 +53,9 @@ namespace osu.Game.Tests.Visual.UserInterface Metadata = { Artist = "very very very very very very very very very very verry long artist", - ArtistUnicode = "very very very very very very very very very very verry long artist", + ArtistUnicode = "very very very very very very very very very very verry long artist unicode", Title = "very very very very very verry long title", - TitleUnicode = "very very very very very verry long title", + TitleUnicode = "very very very very very verry long title unicode", } })); @@ -61,9 +64,9 @@ namespace osu.Game.Tests.Visual.UserInterface Metadata = { Artist = "very very very very very very very very very very verrry long artist", - ArtistUnicode = "very very very very very very very very very very verrry long artist", + ArtistUnicode = "not very long artist unicode", Title = "very very very very very verrry long title", - TitleUnicode = "very very very very very verrry long title", + TitleUnicode = "not very long title unicode", } })); From 359238395f273686b611ac87f5ae1ceef1072a04 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 5 May 2024 15:17:03 -0700 Subject: [PATCH 396/581] Fix now playing overlay text scroll breaking when toggling metadata language setting --- osu.Game/Overlays/NowPlayingOverlay.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 29052ace8e..76c8c237d5 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -479,6 +480,11 @@ namespace osu.Game.Overlays private OsuSpriteText mainSpriteText = null!; private OsuSpriteText fillerSpriteText = null!; + private Bindable showUnicode = null!; + + [Resolved] + private FrameworkConfigManager frameworkConfig { get; set; } = null!; + private LocalisableString text; public LocalisableString Text @@ -531,6 +537,9 @@ namespace osu.Game.Overlays { base.LoadComplete(); + showUnicode = frameworkConfig.GetBindable(FrameworkSetting.ShowUnicode); + showUnicode.BindValueChanged(_ => updateText()); + updateFontAndText(); } From 12cd3bbe1c79d7f2134ed79f03cb1e8005086d99 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 May 2024 11:34:37 +0800 Subject: [PATCH 397/581] Fix incorrect scale handling due to selection point changes --- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 8b9e6436b1..75bb77fa73 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.SkinEditor var drawableItem = (Drawable)b.Item; // each drawable's relative position should be maintained in the scaled quad. - var screenPosition = b.ScreenSpaceSelectionPoint; + var screenPosition = drawableItem.ToScreenSpace(drawableItem.OriginPosition); var relativePositionInOriginal = new Vector2( From 4c7e6b125cdc889b12039b8b4048ab09afc963b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 08:49:30 +0200 Subject: [PATCH 398/581] Add clarification comment --- osu.Game/Overlays/Mods/ModPresetPanel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index 450c684e54..568ca5ecc9 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.cs @@ -55,6 +55,9 @@ namespace osu.Game.Overlays.Mods protected override void Select() { + // this implicitly presumes that if a system mod declares incompatibility with a non-system mod, + // the non-system mod should take precedence. + // if this assumption is ever broken, this should be reconsidered. var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System && !mod.IncompatibleMods.Any(t => Preset.Value.Mods.Any(t.IsInstanceOfType))); From cb4af794161d325143e4ba4421774f746137b51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 08:53:41 +0200 Subject: [PATCH 399/581] Touch up test case --- .../UserInterface/TestSceneModPresetPanel.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs index 9a141e0df0..f87d8e0d2b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetPanel.cs @@ -124,17 +124,17 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestActivatingPresetWithAutoplayWhenSystemModEnabled() + public void TestSystemModsNotPreservedIfIncompatibleWithPresetMods() { ModPresetPanel? panel = null; AddStep("create panel", () => Child = panel = new ModPresetPanel(new ModPreset { - Name = "Autoplay include", + Name = "Autopilot included", Description = "no way", Mods = new Mod[] { - new OsuModAutoplay() + new OsuModAutopilot() }, Ruleset = new OsuRuleset().RulesetInfo }.ToLiveUnmanaged()) @@ -144,20 +144,20 @@ namespace osu.Game.Tests.Visual.UserInterface Width = 0.5f }); - AddStep("Add touch device to selected mod", () => SelectedMods.Value = new Mod[] { new OsuModTouchDevice() }); + AddStep("Add touch device to selected mods", () => SelectedMods.Value = new Mod[] { new OsuModTouchDevice() }); AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); - // touch device should be removed due to incompatible with autoplay. - assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutoplay() }); + // touch device should be removed due to incompatibility with autopilot. + assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutopilot() }); AddStep("deactivate panel", () => panel.AsNonNull().TriggerClick()); assertSelectedModsEquivalentTo(Array.Empty()); - // just for test purpose + // just for test purposes, can't/shouldn't happen in reality AddStep("Add score v2 to selected mod", () => SelectedMods.Value = new Mod[] { new ModScoreV2() }); AddStep("activate panel", () => panel.AsNonNull().TriggerClick()); - assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutoplay(), new ModScoreV2() }); + assertSelectedModsEquivalentTo(new Mod[] { new OsuModAutopilot(), new ModScoreV2() }); } private void assertSelectedModsEquivalentTo(IEnumerable mods) From cf87e9cd40008cedba6f0e183f5c07bf0978643f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 11:22:56 +0200 Subject: [PATCH 400/581] Do not show integration settings on mobile Closes https://github.com/ppy/osu/issues/28097. The settings weren't actually doing anything at all there anyway. --- osu.Game/Overlays/Settings/Sections/OnlineSection.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index 1484f2c756..6593eb69fa 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.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.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; @@ -25,8 +26,10 @@ namespace osu.Game.Overlays.Settings.Sections { new WebSettings(), new AlertsAndPrivacySettings(), - new IntegrationSettings() }; + + if (RuntimeInfo.IsDesktop) + Add(new IntegrationSettings()); } } } From f066026503b4f50377fe3eca5ee55f1261ff00f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 11:55:49 +0200 Subject: [PATCH 401/581] Fix sizing of gameplay preview in skin editor not updating on scaling mode change Closes https://github.com/ppy/osu/issues/28115. --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 2f4820e207..748e9c6c0b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Layout; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -30,7 +31,6 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osu.Game.Users; using osu.Game.Utils; -using osuTK; namespace osu.Game.Overlays.SkinEditor { @@ -70,12 +70,14 @@ namespace osu.Game.Overlays.SkinEditor private OsuScreen? lastTargetScreen; private InvokeOnDisposal? nestedInputManagerDisable; - private Vector2 lastDrawSize; + private LayoutValue drawSizeLayout; public SkinEditorOverlay(ScalingContainer scalingContainer) { this.scalingContainer = scalingContainer; RelativeSizeAxes = Axes.Both; + + AddLayout(drawSizeLayout = new LayoutValue(Invalidation.DrawSize)); } [BackgroundDependencyLoader] @@ -199,10 +201,10 @@ namespace osu.Game.Overlays.SkinEditor { base.Update(); - if (game.DrawSize != lastDrawSize) + if (!drawSizeLayout.IsValid) { - lastDrawSize = game.DrawSize; updateScreenSizing(); + drawSizeLayout.Validate(); } } From 353a07f7a5fa4a24c3d91b545defda6f8128efe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 12:23:11 +0200 Subject: [PATCH 402/581] Fix code quality inspection --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 748e9c6c0b..571f99bd08 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.SkinEditor private OsuScreen? lastTargetScreen; private InvokeOnDisposal? nestedInputManagerDisable; - private LayoutValue drawSizeLayout; + private readonly LayoutValue drawSizeLayout; public SkinEditorOverlay(ScalingContainer scalingContainer) { From 4d9ccdc3b2391463355d297387c12181f72a6fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 13:23:41 +0200 Subject: [PATCH 403/581] Encode user ID to replays --- osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs index 32c18b3af2..b71d9d916e 100644 --- a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs @@ -43,6 +43,9 @@ namespace osu.Game.Scoring.Legacy [JsonConverter(typeof(StringEnumConverter))] public ScoreRank? Rank; + [JsonProperty("user_id")] + public int UserID = -1; + public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo { OnlineID = score.OnlineID, @@ -51,6 +54,7 @@ namespace osu.Game.Scoring.Legacy MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(), ClientVersion = score.ClientVersion, Rank = score.Rank, + UserID = score.UserID, }; } } From 554ead0d9dd596a01bf55ab36c72f8eb346f81bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 May 2024 13:34:02 +0200 Subject: [PATCH 404/581] Decode user ID from score if available --- .../Beatmaps/Formats/LegacyScoreDecoderTest.cs | 9 +++++++++ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 ++ 2 files changed, 11 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index 383c08c10f..cc7b37e6a8 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Legacy; using osu.Game.IO.Legacy; +using osu.Game.Models; using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; @@ -31,6 +32,7 @@ using osu.Game.Rulesets.Taiko; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Tests.Resources; +using osu.Game.Users; namespace osu.Game.Tests.Beatmaps.Formats { @@ -224,6 +226,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } }; scoreInfo.OnlineID = 123123; + scoreInfo.RealmUser = new RealmUser + { + Username = "spaceman_atlas", + OnlineID = 3035836, + CountryCode = CountryCode.PL + }; scoreInfo.ClientVersion = "2023.1221.0"; var beatmap = new TestBeatmap(ruleset); @@ -248,6 +256,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics)); Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods)); Assert.That(decodedAfterEncode.ScoreInfo.ClientVersion, Is.EqualTo("2023.1221.0")); + Assert.That(decodedAfterEncode.ScoreInfo.RealmUser.OnlineID, Is.EqualTo(3035836)); }); } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index b0d7087ed1..00e294fdcd 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -131,6 +131,8 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray(); score.ScoreInfo.ClientVersion = readScore.ClientVersion; decodedRank = readScore.Rank; + if (readScore.UserID > 1) + score.ScoreInfo.RealmUser.OnlineID = readScore.UserID; }); } } From bd869b6cdcd41c3406022fb784f54fe2fdebf849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 13:24:24 +0200 Subject: [PATCH 405/581] Add failing tests for looking up users by online ID if present when importing scores --- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 82 ++++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index eb2c098ab8..9c72804a6b 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -287,7 +287,7 @@ namespace osu.Game.Tests.Scores.IO } [Test] - public void TestUserLookedUpForOnlineScore() + public void TestUserLookedUpByUsernameForOnlineScoreIfUserIDMissing() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { @@ -301,6 +301,9 @@ namespace osu.Game.Tests.Scores.IO switch (req) { case GetUserRequest userRequest: + if (userRequest.Lookup != "Test user") + return false; + userRequest.TriggerSuccess(new APIUser { Username = "Test user", @@ -350,7 +353,7 @@ namespace osu.Game.Tests.Scores.IO } [Test] - public void TestUserLookedUpForLegacyOnlineScore() + public void TestUserLookedUpByUsernameForLegacyOnlineScore() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { @@ -364,6 +367,9 @@ namespace osu.Game.Tests.Scores.IO switch (req) { case GetUserRequest userRequest: + if (userRequest.Lookup != "Test user") + return false; + userRequest.TriggerSuccess(new APIUser { Username = "Test user", @@ -413,7 +419,7 @@ namespace osu.Game.Tests.Scores.IO } [Test] - public void TestUserNotLookedUpForOfflineScore() + public void TestUserNotLookedUpForOfflineScoreIfUserIDMissing() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) { @@ -427,6 +433,9 @@ namespace osu.Game.Tests.Scores.IO switch (req) { case GetUserRequest userRequest: + if (userRequest.Lookup != "Test user") + return false; + userRequest.TriggerSuccess(new APIUser { Username = "Test user", @@ -476,6 +485,73 @@ namespace osu.Game.Tests.Scores.IO } } + [Test] + public void TestUserLookedUpByOnlineIDIfPresent([Values] bool isOnlineScore) + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost()) + { + try + { + var osu = LoadOsuIntoHost(host, true); + + var api = (DummyAPIAccess)osu.API; + api.HandleRequest = req => + { + switch (req) + { + case GetUserRequest userRequest: + if (userRequest.Lookup != "5555") + return false; + + userRequest.TriggerSuccess(new APIUser + { + Username = "Some other guy", + CountryCode = CountryCode.DE, + Id = 5555 + }); + return true; + + default: + return false; + } + }; + + var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely(); + + var toImport = new ScoreInfo + { + Rank = ScoreRank.B, + TotalScore = 987654, + Accuracy = 0.8, + MaxCombo = 500, + Combo = 250, + User = new APIUser { Id = 5555 }, + Date = DateTimeOffset.Now, + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = beatmap.Beatmaps.First() + }; + if (isOnlineScore) + toImport.OnlineID = 12345; + + var imported = LoadScoreIntoOsu(osu, toImport); + + Assert.AreEqual(toImport.Rank, imported.Rank); + Assert.AreEqual(toImport.TotalScore, imported.TotalScore); + Assert.AreEqual(toImport.Accuracy, imported.Accuracy); + Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo); + Assert.AreEqual(toImport.Date, imported.Date); + Assert.AreEqual(toImport.OnlineID, imported.OnlineID); + Assert.AreEqual("Some other guy", imported.RealmUser.Username); + Assert.AreEqual(5555, imported.RealmUser.OnlineID); + Assert.AreEqual(CountryCode.DE, imported.RealmUser.CountryCode); + } + finally + { + host.Exit(); + } + } + } + public static ScoreInfo LoadScoreIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null) { // clone to avoid attaching the input score to realm. From abfb2c00bcaaf071b264f82e5205350ec7a5edf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 13:24:34 +0200 Subject: [PATCH 406/581] Look up users by ID if available when importing scores --- osu.Game/Scoring/ScoreImporter.cs | 71 ++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 4ae8e51f6d..69c53af16f 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -103,6 +103,14 @@ namespace osu.Game.Scoring } // Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores). + + // TODO: `UserLookupCache` cannot currently be used here because of async foibles. + // It only supports lookups by user ID (username would require web changes), and even then the ID lookups cannot be used. + // That is because that component provides an async interface, and async functions cannot be consumed safely here due to the rigid structure of `RealmArchiveModelImporter`. + // The importer has two paths, one async and one sync; the async path runs the sync path in a task. + // This means that sometimes `PostImport()` is called from a sync context, and sometimes from an async one, whilst itself being a sync method. + // That in turn makes `.GetResultSafely()` not callable inside `PostImport()`, as it will throw when called from an async context, + private readonly Dictionary idLookupCache = new Dictionary(); private readonly Dictionary usernameLookupCache = new Dictionary(); protected override void PostImport(ScoreInfo model, Realm realm, ImportParameters parameters) @@ -127,24 +135,34 @@ namespace osu.Game.Scoring if (model.RealmUser.OnlineID == APIUser.SYSTEM_USER_ID) return; - if (model.OnlineID < 0 && model.LegacyOnlineID <= 0) - return; - - string username = model.RealmUser.Username; - - if (usernameLookupCache.TryGetValue(username, out var existing)) + if (model.RealmUser.OnlineID > 1) { - model.User = existing; + model.User = lookupUserById(model.RealmUser.OnlineID) ?? model.User; return; } - var userRequest = new GetUserRequest(username); + if (model.OnlineID < 0 && model.LegacyOnlineID <= 0) + return; + + model.User = lookupUserByName(model.RealmUser.Username) ?? model.User; + } + + private APIUser? lookupUserById(int id) + { + if (idLookupCache.TryGetValue(id, out var existing)) + { + return existing; + } + + var userRequest = new GetUserRequest(id); api.Perform(userRequest); if (userRequest.Response is APIUser user) { - usernameLookupCache.TryAdd(username, new APIUser + APIUser cachedUser; + + idLookupCache.TryAdd(id, cachedUser = new APIUser { // Because this is a permanent cache, let's only store the pieces we're interested in, // rather than the full API response. If we start to store more than these three fields @@ -154,8 +172,41 @@ namespace osu.Game.Scoring CountryCode = user.CountryCode, }); - model.User = user; + return cachedUser; } + + return null; + } + + private APIUser? lookupUserByName(string username) + { + if (usernameLookupCache.TryGetValue(username, out var existing)) + { + return existing; + } + + var userRequest = new GetUserRequest(username); + + api.Perform(userRequest); + + if (userRequest.Response is APIUser user) + { + APIUser cachedUser; + + usernameLookupCache.TryAdd(username, cachedUser = new APIUser + { + // Because this is a permanent cache, let's only store the pieces we're interested in, + // rather than the full API response. If we start to store more than these three fields + // in realm, this should be undone. + Id = user.Id, + Username = user.Username, + CountryCode = user.CountryCode, + }); + + return cachedUser; + } + + return null; } } } From 9c6968c13a2c6a1a0392649dddd1c0cfb7828f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 May 2024 16:29:03 +0200 Subject: [PATCH 407/581] Use `score.User.OnlineID` instead of `score.UserID` You'd hope that they'd be the same thing, but post-https://github.com/ppy/osu-server-spectator/pull/230 it turns out that cannot be guaranteed, so just attempt to use `User` in the encoder consistently everywhere... --- osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs index b71d9d916e..60bec687f4 100644 --- a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs @@ -54,7 +54,7 @@ namespace osu.Game.Scoring.Legacy MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(), ClientVersion = score.ClientVersion, Rank = score.Rank, - UserID = score.UserID, + UserID = score.User.OnlineID, }; } } From a694f4625309667534c4a76e794ce0728c4b5b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Wolfschl=C3=A4ger?= Date: Mon, 6 May 2024 18:10:58 +0200 Subject: [PATCH 408/581] Add new localisable strings --- .../DeleteConfirmationContentStrings.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 osu.Game/Localisation/DeleteConfirmationContentStrings.cs diff --git a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs new file mode 100644 index 0000000000..26b7133456 --- /dev/null +++ b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class DeleteConfirmationContentStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.DeleteConfirmationContent"; + + /// + /// "All beatmaps?" + /// + public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"All beatmaps?"); + + /// + /// "All beatmaps videos? This cannot be undone!" + /// + public static LocalisableString BeatmapVideos => new TranslatableString(getKey(@"beatmap_videos"), @"All beatmaps videos? This cannot be undone!"); + + /// + /// "All skins? This cannot be undone!" + /// + public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"All skins? This cannot be undone!"); + + /// + /// "All collections? This cannot be undone!" + /// + public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"All collections? This cannot be undone!"); + + /// + /// "All scores? This cannot be undone!" + /// + public static LocalisableString Scores => new TranslatableString(getKey(@"collections"), @"All scores? This cannot be undone!"); + + /// + /// "All mod presets?" + /// + public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"All mod presets?"); + + + private static string getKey(string key) => $@"{prefix}:{key}"; + + } +} From 32444e0e30f10e4baadc6aac971c2f66f0bfbce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Wolfschl=C3=A4ger?= Date: Mon, 6 May 2024 18:11:40 +0200 Subject: [PATCH 409/581] Make deletion confirmation content less confusing --- .../Sections/Maintenance/BeatmapSettings.cs | 6 +++--- .../Sections/Maintenance/CollectionsSettings.cs | 2 +- .../Maintenance/MassDeleteConfirmationDialog.cs | 5 +++-- .../MassVideoDeleteConfirmationDialog.cs | 16 ---------------- .../Sections/Maintenance/ModPresetSettings.cs | 2 +- .../Sections/Maintenance/ScoreSettings.cs | 2 +- .../Sections/Maintenance/SkinSettings.cs | 2 +- 7 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs index 4b1836ed86..d0a8fc7d2c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/BeatmapSettings.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { deleteBeatmapsButton.Enabled.Value = false; Task.Run(() => beatmaps.Delete()).ContinueWith(_ => Schedule(() => deleteBeatmapsButton.Enabled.Value = true)); - })); + }, DeleteConfirmationContentStrings.Beatmaps)); } }); @@ -40,11 +40,11 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Text = MaintenanceSettingsStrings.DeleteAllBeatmapVideos, Action = () => { - dialogOverlay?.Push(new MassVideoDeleteConfirmationDialog(() => + dialogOverlay?.Push(new MassDeleteConfirmationDialog(() => { deleteBeatmapVideosButton.Enabled.Value = false; Task.Run(beatmaps.DeleteAllVideos).ContinueWith(_ => Schedule(() => deleteBeatmapVideosButton.Enabled.Value = true)); - })); + }, DeleteConfirmationContentStrings.BeatmapVideos)); } }); AddRange(new Drawable[] diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs index b373535a8b..b1c44aa93c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/CollectionsSettings.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Text = MaintenanceSettingsStrings.DeleteAllCollections, Action = () => { - dialogOverlay?.Push(new MassDeleteConfirmationDialog(deleteAllCollections)); + dialogOverlay?.Push(new MassDeleteConfirmationDialog(deleteAllCollections, DeleteConfirmationContentStrings.Collections)); } }); } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs index 99ef62d94b..7ead815fe9 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MassDeleteConfirmationDialog.cs @@ -2,15 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Localisation; using osu.Game.Overlays.Dialog; namespace osu.Game.Overlays.Settings.Sections.Maintenance { public partial class MassDeleteConfirmationDialog : DangerousActionDialog { - public MassDeleteConfirmationDialog(Action deleteAction) + public MassDeleteConfirmationDialog(Action deleteAction, LocalisableString deleteContent) { - BodyText = "Everything?"; + BodyText = deleteContent; DangerousAction = deleteAction; } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs deleted file mode 100644 index 6312e09b3e..0000000000 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MassVideoDeleteConfirmationDialog.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Overlays.Settings.Sections.Maintenance -{ - public partial class MassVideoDeleteConfirmationDialog : MassDeleteConfirmationDialog - { - public MassVideoDeleteConfirmationDialog(Action deleteAction) - : base(deleteAction) - { - BodyText = "All beatmap videos? This cannot be undone!"; - } - } -} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs index f0d6d10e51..9c55308abe 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ModPresetSettings.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { deleteAllButton.Enabled.Value = false; Task.Run(deleteAllModPresets).ContinueWith(t => Schedule(onAllModPresetsDeleted, t)); - })); + }, DeleteConfirmationContentStrings.ModPresets)); } }, undeleteButton = new SettingsButton diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs index c6f4f1e1a5..235f239c7c 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/ScoreSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { deleteScoresButton.Enabled.Value = false; Task.Run(() => scores.Delete()).ContinueWith(_ => Schedule(() => deleteScoresButton.Enabled.Value = true)); - })); + }, DeleteConfirmationContentStrings.Scores)); } }); } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs index c3ac49af6d..e962118a36 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/SkinSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { deleteSkinsButton.Enabled.Value = false; Task.Run(() => skins.Delete()).ContinueWith(_ => Schedule(() => deleteSkinsButton.Enabled.Value = true)); - })); + }, DeleteConfirmationContentStrings.Skins)); } }); } From 12522d9ae5dc1ef36055a7698518231f8a74ebbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Wolfschl=C3=A4ger?= Date: Mon, 6 May 2024 18:29:46 +0200 Subject: [PATCH 410/581] Fix formatting issues --- osu.Game/Localisation/DeleteConfirmationContentStrings.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs index 26b7133456..d9e90675f7 100644 --- a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs +++ b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. - using osu.Framework.Localisation; namespace osu.Game.Localisation @@ -40,8 +39,6 @@ namespace osu.Game.Localisation /// public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"All mod presets?"); - private static string getKey(string key) => $@"{prefix}:{key}"; - } } From f824bd14414e9e37af05a9dba130c02c7aa5a62d Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 6 May 2024 21:52:03 +0200 Subject: [PATCH 411/581] Fix `userTriggered` not being passed to private helper --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 5bf28ae79b..67fd6a9550 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -540,7 +540,7 @@ namespace osu.Game.Overlays.SkinEditor protected void Redo() => changeHandler?.RestoreState(1); - public void Save(bool userTriggered = true) => save(currentSkin.Value); + public void Save(bool userTriggered = true) => save(currentSkin.Value, userTriggered); private void save(Skin skin, bool userTriggered = true) { From 91c684151a740005e24c3c52ff188772489b5171 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 May 2024 14:16:25 +0800 Subject: [PATCH 412/581] Update bundled beatmaps --- .../Drawables/BundledBeatmapDownloader.cs | 504 +++++++++++------- 1 file changed, 312 insertions(+), 192 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs index 21ab1b78ea..3aa34a5580 100644 --- a/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BundledBeatmapDownloader.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.RegularExpressions; using osu.Framework.Allocation; @@ -21,6 +22,8 @@ using osu.Game.Utils; namespace osu.Game.Beatmaps.Drawables { + [SuppressMessage("ReSharper", "StringLiteralTypo")] + [SuppressMessage("ReSharper", "CommentTypo")] public partial class BundledBeatmapDownloader : CompositeDrawable { private readonly bool shouldPostNotifications; @@ -50,7 +53,7 @@ namespace osu.Game.Beatmaps.Drawables { queueDownloads(always_bundled_beatmaps); - queueDownloads(bundled_osu, 8); + queueDownloads(bundled_osu, 6); queueDownloads(bundled_taiko, 3); queueDownloads(bundled_catch, 3); queueDownloads(bundled_mania, 3); @@ -128,6 +131,26 @@ namespace osu.Game.Beatmaps.Drawables } } + /* + * criteria for bundled maps (managed by pishifat) + * + * auto: + * - licensed song + * - includes ENHI diffs + * - between 60s and 240s + * + * manual: + * - bg is explicitly permitted as okay to use. lots of artists say some variation of "it's ok for personal use/non-commercial use/with credit" + * (which is prob fine when maps are presented as user-generated content), but for a new osu! player, it's easy to assume bundled maps are + * commercial content like other rhythm games, so it's best to be cautious about using not-explicitly-permitted artwork. + * + * - no ai/thirst bgs + * - no controversial/explicit song content or titles + * - no repeating bundled songs (within each mode) + * - no songs that are relatively low production value + * - no songs with limited accessibility (annoying high pitch vocals, noise rock, etc) + */ + private const string tutorial_filename = "1011011 nekodex - new beginnings.osz"; /// @@ -135,215 +158,312 @@ namespace osu.Game.Beatmaps.Drawables /// private static readonly string[] always_bundled_beatmaps = { - // This thing is 40mb, I'm not sure we want it here... + // winner of https://osu.ppy.sh/home/news/2013-09-06-osu-monthly-beatmapping-contest-1 + @"123593 Rostik - Liquid (Paul Rosenthal Remix).osz", + // winner of https://osu.ppy.sh/home/news/2013-10-28-monthly-beatmapping-contest-2-submissions-open + @"140662 cYsmix feat. Emmy - Tear Rain.osz", + // winner of https://osu.ppy.sh/home/news/2013-12-15-monthly-beatmapping-contest-3-submissions-open + @"151878 Chasers - Lost.osz", + // winner of https://osu.ppy.sh/home/news/2014-02-14-monthly-beatmapping-contest-4-submissions-now + @"163112 Kuba Oms - My Love.osz", + // winner of https://osu.ppy.sh/home/news/2014-05-07-monthly-beatmapping-contest-5-submissions-now + @"190390 Rameses B - Flaklypa.osz", + // winner of https://osu.ppy.sh/home/news/2014-09-24-monthly-beatmapping-contest-7 + @"241526 Soleily - Renatus.osz", + // winner of https://osu.ppy.sh/home/news/2015-02-11-monthly-beatmapping-contest-8 + @"299224 raja - the light.osz", + // winner of https://osu.ppy.sh/home/news/2015-04-13-monthly-beatmapping-contest-9-taiko-only + @"319473 Furries in a Blender - Storm World.osz", + // winner of https://osu.ppy.sh/home/news/2015-06-15-monthly-beatmapping-contest-10-ctb-only + @"342751 Hylian Lemon - Foresight Is for Losers.osz", + // winner of https://osu.ppy.sh/home/news/2015-08-22-monthly-beatmapping-contest-11-mania-only + @"385056 Toni Leys - Dragon Valley (Toni Leys Remix feat. Esteban Bellucci).osz", + // winner of https://osu.ppy.sh/home/news/2016-03-04-beatmapping-contest-12-osu + @"456054 IAHN - Candy Luv (Short Ver.).osz", + // winner of https://osu.ppy.sh/home/news/2020-11-30-a-labour-of-love + // (this thing is 40mb, I'm not sure if we want it here...) @"1388906 Raphlesia & BilliumMoto - My Love.osz", - // Winner of Triangles mapping competition: https://osu.ppy.sh/home/news/2022-10-06-results-triangles + // winner of https://osu.ppy.sh/home/news/2022-05-31-triangles @"1841885 cYsmix - triangles.osz", + // winner of https://osu.ppy.sh/home/news/2023-02-01-twin-trials-contest-beatmapping-phase + @"1971987 James Landino - Aresene's Bazaar.osz", }; private static readonly string[] bundled_osu = { - "682286 Yuyoyuppe - Emerald Galaxy.osz", - "682287 baker - For a Dead Girl+.osz", - "682289 Hige Driver - I Wanna Feel Your Love (feat. shully).osz", - "682290 Hige Driver - Miracle Sugite Yabai (feat. shully).osz", - "682416 Hige Driver - Palette.osz", - "682595 baker - Kimi ga Kimi ga -vocanico remix-.osz", - "716211 yuki. - Spring Signal.osz", - "716213 dark cat - BUBBLE TEA (feat. juu & cinders).osz", - "716215 LukHash - CLONED.osz", - "716219 IAHN - Snowdrop.osz", - "716249 *namirin - Senaka Awase no Kuukyo (with Kakichoco).osz", - "716390 sakuraburst - SHA.osz", - "716441 Fractal Dreamers - Paradigm Shift.osz", - "729808 Thaehan - Leprechaun.osz", - "751771 Cranky - Hanaarashi.osz", - "751772 Cranky - Ran.osz", - "751773 Cranky - Feline, the White....osz", - "751774 Function Phantom - Variable.osz", - "751779 Rin - Daishibyo set 14 ~ Sado no Futatsuiwa.osz", - "751782 Fractal Dreamers - Fata Morgana.osz", - "751785 Cranky - Chandelier - King.osz", - "751846 Fractal Dreamers - Celestial Horizon.osz", - "751866 Rin - Moriya set 08 ReEdit ~ Youkai no Yama.osz", - "751894 Fractal Dreamers - Blue Haven.osz", - "751896 Cranky - Rave 2 Rave.osz", - "751932 Cranky - La fuite des jours.osz", - "751972 Cranky - CHASER.osz", - "779173 Thaehan - Superpower.osz", - "780932 VINXIS - A Centralized View.osz", - "785572 S3RL - I'll See You Again (feat. Chi Chi).osz", - "785650 yuki. feat. setsunan - Hello! World.osz", - "785677 Dictate - Militant.osz", - "785731 S3RL - Catchit (Radio Edit).osz", - "785774 LukHash - GLITCH.osz", - "786498 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", - "789374 Pulse - LP.osz", - "789528 James Portland - Sky.osz", - "789529 Lexurus - Gravity.osz", - "789544 Andromedik - Invasion.osz", - "789905 Gourski x Himmes - Silence.osz", - "791667 cYsmix - Babaroque (Short Ver.).osz", - "791798 cYsmix - Behind the Walls.osz", - "791845 cYsmix - Little Knight.osz", - "792241 cYsmix - Eden.osz", - "792396 cYsmix - The Ballad of a Mindless Girl.osz", - "795432 Phonetic - Journey.osz", - "831322 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", - "847764 Cranky - Crocus.osz", - "847776 Culprate & Joe Ford - Gaucho.osz", - "847812 J. Pachelbel - Canon (Cranky Remix).osz", - "847900 Cranky - Time Alter.osz", - "847930 LukHash - 8BIT FAIRY TALE.osz", - "848003 Culprate - Aurora.osz", - "848068 nanobii - popsicle beach.osz", - "848090 Trial & Error - DAI*TAN SENSATION feat. Nanahira, Mii, Aitsuki Nakuru (Short Ver.).osz", - "848259 Culprate & Skorpion - Jester.osz", - "848976 Dictate - Treason.osz", - "851543 Culprate - Florn.osz", - "864748 Thaehan - Angry Birds Epic (Remix).osz", - "873667 OISHII - ONIGIRI FREEWAY.osz", - "876227 Culprate, Keota & Sophie Meiers - Mechanic Heartbeat.osz", - "880487 cYsmix - Peer Gynt.osz", - "883088 Wisp X - Somewhere I'd Rather Be.osz", - "891333 HyuN - White Aura.osz", - "891334 HyuN - Wild Card.osz", - "891337 HyuN feat. LyuU - Cross Over.osz", - "891338 HyuN & Ritoru - Apocalypse in Love.osz", - "891339 HyuN feat. Ato - Asu wa Ame ga Yamukara.osz", - "891345 HyuN - Infinity Heaven.osz", - "891348 HyuN - Guitian.osz", - "891356 HyuN - Legend of Genesis.osz", - "891366 HyuN - Illusion of Inflict.osz", - "891417 HyuN feat. Yu-A - My life is for you.osz", - "891441 HyuN - You'Re aRleAdY dEAd.osz", - "891632 HyuN feat. YURI - Disorder.osz", - "891712 HyuN - Tokyo's Starlight.osz", - "901091 *namirin - Ciel etoile.osz", - "916990 *namirin - Koishiteiku Planet.osz", - "929284 tieff - Sense of Nostalgia.osz", - "933940 Ben Briggs - Yes (Maybe).osz", - "934415 Ben Briggs - Fearless Living.osz", - "934627 Ben Briggs - New Game Plus.osz", - "934666 Ben Briggs - Wave Island.osz", - "936126 siromaru + cranky - conflict.osz", - "940377 onumi - ARROGANCE.osz", - "940597 tieff - Take Your Swimsuit.osz", - "941085 tieff - Our Story.osz", - "949297 tieff - Sunflower.osz", - "952380 Ben Briggs - Why Are We Yelling.osz", - "954272 *namirin - Kanzen Shouri*Esper Girl.osz", - "955866 KIRA & Heartbreaker - B.B.F (feat. Hatsune Miku & Kagamine Rin).osz", - "961320 Kuba Oms - All In All.osz", - "964553 The Flashbulb - You Take the World's Weight Away.osz", - "965651 Fractal Dreamers - Ad Astra.osz", - "966225 The Flashbulb - Passage D.osz", - "966324 DJ'TEKINA//SOMETHING - Hidamari no Uta.osz", - "972810 James Landino & Kabuki - Birdsong.osz", - "972932 James Landino - Hide And Seek.osz", - "977276 The Flashbulb - Mellann.osz", - "981616 *namirin - Mizutamari Tobikoete (with Nanahira).osz", - "985788 Loki - Wizard's Tower.osz", - "996628 OISHII - ONIGIRI FREEWAY.osz", - "996898 HyuN - White Aura.osz", - "1003554 yuki. - Nadeshiko Sensation.osz", - "1014936 Thaehan - Bwa !.osz", - "1019827 UNDEAD CORPORATION - Sad Dream.osz", - "1020213 Creo - Idolize.osz", - "1021450 Thaehan - Chiptune & Baroque.osz", + @"682286 Yuyoyuppe - Emerald Galaxy.osz", + @"682287 baker - For a Dead Girl+.osz", + @"682595 baker - Kimi ga Kimi ga -vocanico remix-.osz", + @"1048705 Thaehan - Never Give Up.osz", + @"1050185 Carpool Tunnel - Hooked Again.osz", + @"1052846 Carpool Tunnel - Impressions.osz", + @"1062477 Ricky Montgomery - Line Without a Hook.osz", + @"1081119 Celldweller - Pulsar.osz", + @"1086289 Frums - 24eeev0-$.osz", + @"1133317 PUP - Free At Last.osz", + @"1171188 PUP - Full Blown Meltdown.osz", + @"1177043 PUP - My Life Is Over And I Couldn't Be Happier.osz", + @"1250387 Circle of Dust - Humanarchy (Cut Ver.).osz", + @"1255411 Wisp X - Somewhere I'd Rather Be.osz", + @"1320298 nekodex - Little Drummer Girl.osz", + @"1323877 Masahiro ""Godspeed"" Aoki - Blaze.osz", + @"1342280 Minagu feat. Aitsuki Nakuru - Theater Endroll.osz", + @"1356447 SECONDWALL - Boku wa Boku de shika Nakute.osz", + @"1368054 SECONDWALL - Shooting Star.osz", + @"1398580 La priere - Senjou no Utahime.osz", + @"1403962 m108 - Sunflower.osz", + @"1405913 fiend - FEVER DREAM (feat. yzzyx).osz", + @"1409184 Omoi - Hey William (New Translation).osz", + @"1413418 URBANGARDE - KAMING OUT (Cut Ver.).osz", + @"1417793 P4koo (NONE) - Sogaikan Utopia.osz", + @"1428384 DUAL ALTER WORLD - Veracila.osz", + @"1442963 PUP - DVP.osz", + @"1460370 Sound Souler - Empty Stars.osz", + @"1485184 Koven - Love Wins Again.osz", + @"1496811 T & Sugah - Wicked Days (Cut Ver.).osz", + @"1501511 Masahiro ""Godspeed"" Aoki - Frostbite (Cut Ver.).osz", + @"1511518 T & Sugah X Zazu - Lost On My Own (Cut Ver.).osz", + @"1516617 wotoha - Digital Life Hacker.osz", + @"1524273 Michael Cera Palin - Admiral.osz", + @"1564234 P4koo - Fly High (feat. rerone).osz", + @"1572918 Lexurus - Take Me Away (Cut Ver.).osz", + @"1577313 Kurubukko - The 84th Flight.osz", + @"1587839 Amidst - Droplet.osz", + @"1595193 BlackY - Sakura Ranman Cleopatra.osz", + @"1667560 xi - FREEDOM DiVE.osz", + @"1668789 City Girl - L2M (feat. Kelsey Kuan).osz", + @"1672934 xi - Parousia.osz", + @"1673457 Boom Kitty - Any Other Way (feat. Ivy Marie).osz", + @"1685122 xi - Time files.osz", + @"1689372 NIWASHI - Y.osz", + @"1729551 JOYLESS - Dream.osz", + @"1742868 Ritorikal - Synergy.osz", + @"1757511 KINEMA106 - KARASU.osz", + @"1778169 Ricky Montgomery - Cabo.osz", + @"1848184 FRASER EDWARDS - Ruination.osz", + @"1862574 Pegboard Nerds - Try This (Cut Ver.).osz", + @"1873680 happy30 - You spin my world.osz", + @"1890055 A.SAKA - Mutsuki Akari no Yuki.osz", + @"1911933 Marmalade butcher - Waltz for Chroma (feat. Natsushiro Takaaki).osz", + @"1940007 Mili - Ga1ahad and Scientific Witchery.osz", + @"1948970 Shadren - You're Here Forever.osz", + @"1967856 Annabel - alpine blue.osz", + @"1969316 Silentroom - NULCTRL.osz", + @"1978614 Krimek - Idyllic World.osz", + @"1991315 Feint - Tower Of Heaven (You Are Slaves) (Cut Ver.).osz", + @"1997470 tephe - Genjitsu Escape.osz", + @"1999116 soowamisu - .vaporcore.osz", + @"2010589 Junk - Yellow Smile (bms edit).osz", + @"2022054 Yokomin - STINGER.osz", + @"2025686 Aice room - For U.osz", + @"2035357 C-Show feat. Ishizawa Yukari - Border Line.osz", + @"2039403 SECONDWALL - Freedom.osz", + @"2046487 Rameses B - Against the Grain (feat. Veela).osz", + @"2052201 ColBreakz & Vizzen - Remember.osz", + @"2055535 Sephid - Thunderstrike 1988.osz", + @"2057584 SAMString - Ataraxia.osz", + @"2067270 Blue Stahli - The Fall.osz", + @"2075039 garlagan - Skyless.osz", + @"2079089 Hamu feat. yuiko - Innocent Letter.osz", + @"2082895 FATE GEAR - Heart's Grave.osz", + @"2085974 HoneyComeBear - Twilight.osz", + @"2094934 F.O.O.L & Laura Brehm - Waking Up.osz", + @"2097481 Mameyudoufu - Wave feat. Aitsuki Nakuru.osz", + @"2106075 MYUKKE. - The 89's Momentum.osz", + @"2117392 t+pazolite & Komiya Mao - Elustametat.osz", + @"2123533 LeaF - Calamity Fortune.osz", + @"2143876 Alkome - Your Voice.osz", + @"2145826 Sephid - Cross-D Skyline.osz", + @"2153172 Emiru no Aishita Tsukiyo ni Dai San Gensou Kyoku wo - Eternal Bliss.osz", }; private static readonly string[] bundled_taiko = { - "707824 Fractal Dreamers - Fortuna Redux.osz", - "789553 Cranky - Ran.osz", - "827822 Function Phantom - Neuronecia.osz", - "847323 Nakanojojo - Bittersweet (feat. Kuishinboakachan a.k.a Kiato).osz", - "847433 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI.osz", - "847576 dark cat - hot chocolate.osz", - "847957 Wisp X - Final Moments.osz", - "876282 VINXIS - Greetings.osz", - "876648 Thaehan - Angry Birds Epic (Remix).osz", - "877069 IAHN - Transform (Original Mix).osz", - "877496 Thaehan - Leprechaun.osz", - "877935 Thaehan - Overpowered.osz", - "878344 yuki. - Be Your Light.osz", - "918446 VINXIS - Facade.osz", - "918903 LukHash - Ghosts.osz", - "919251 *namirin - Hitokoto no Kyori.osz", - "919704 S3RL - I Will Pick You Up (feat. Tamika).osz", - "921535 SOOOO - Raven Haven.osz", - "927206 *namirin - Kanzen Shouri*Esper Girl.osz", - "927544 Camellia feat. Nanahira - Kansoku Eisei.osz", - "930806 Nakanojojo - Pararara (feat. Amekoya).osz", - "931741 Camellia - Quaoar.osz", - "935699 Rin - Mythic set ~ Heart-Stirring Urban Legends.osz", - "935732 Thaehan - Yuujou.osz", - "941145 Function Phantom - Euclid.osz", - "942334 Dictate - Cauldron.osz", - "946540 nanobii - astral blast.osz", - "948844 Rin - Kishinjou set 01 ~ Mist Lake.osz", - "949122 Wisp X - Petal.osz", - "951618 Rin - Kishinjou set 02 ~ Mermaid from the Uncharted Land.osz", - "957412 Rin - Lunatic set 16 ~ The Space Shrine Maiden Returns Home.osz", - "961335 Thaehan - Insert Coin.osz", - "965178 The Flashbulb - DIDJ PVC.osz", - "966087 The Flashbulb - Creep.osz", - "966277 The Flashbulb - Amen Iraq.osz", - "966407 LukHash - ROOM 12.osz", - "966451 The Flashbulb - Six Acid Strings.osz", - "972301 BilliumMoto - four veiled stars.osz", - "973173 nanobii - popsicle beach.osz", - "973954 BilliumMoto - Rocky Buinne (Short Ver.).osz", - "975435 BilliumMoto - life flashes before weeb eyes.osz", - "978759 L. V. Beethoven - Moonlight Sonata (Cranky Remix).osz", - "982559 BilliumMoto - HDHR.osz", - "984361 The Flashbulb - Ninedump.osz", - "1023681 Inferi - The Ruin of Mankind.osz", - "1034358 ALEPH - The Evil Spirit.osz", - "1037567 ALEPH - Scintillations.osz", + "1048153 Chroma - [@__@].osz", + "1229307 Venetian Snares - Shaky Sometimes.osz", + "1236083 meganeko - Sirius A (osu! edit).osz", + "1248594 Noisia - Anomaly.osz", + "1272851 siqlo - One Way Street.osz", + "1290736 Kola Kid - good old times.osz", + "1318825 SECONDWALL - Light.osz", + "1320872 MYUKKE. - The 89's Momentum.osz", + "1337389 cute girls doing cute things - Main Heroine.osz", + "1397782 Reku Mochizuki - Yorixiro.osz", + "1407228 II-L - VANGUARD-1.osz", + "1422686 II-L - VANGUARD-2.osz", + "1429217 Street - Phi.osz", + "1442235 2ToneDisco x Cosmicosmo - Shoelaces (feat. Puniden).osz", + "1447478 Cres. - End Time.osz", + "1449942 m108 - Crescent Sakura.osz", + "1463778 MuryokuP - A tree without a branch.osz", + "1465152 fiend - Fever Dream (feat. yzzyx).osz", + "1472397 MYUKKE. - Boudica.osz", + "1488148 Aoi vs. siqlo - Hacktivism.osz", + "1522733 wotoha - Digital Life Hacker.osz", + "1540010 Marmalade butcher - Floccinaucinihilipilification.osz", + "1584690 MYUKKE. - AKKERA-COUNTRY-BOY.osz", + "1608857 BLOOD STAIN CHILD - S.O.P.H.I.A.osz", + "1609365 Reku Mochizuki - Faith of Eastward.osz", + "1622545 METAROOM - I - DINKI THE STARGUIDE.osz", + "1629336 METAROOM - PINK ORIGINS.osz", + "1644680 Neko Hacker - Pictures feat. 4s4ki.osz", + "1650835 RiraN - Ready For The Madness.osz", + "1661508 PTB10 - Starfall.osz", + "1671987 xi - World Fragments II.osz", + "1703065 tokiwa - wasurena feat. Sennzai.osz", + "1703527 tokiwa feat. Nakamura Sanso - Kotodama Refrain.osz", + "1704340 A-One feat. Shihori - Magic Girl !!.osz", + "1712783 xi - Parousia.osz", + "1718774 Harumaki Gohan - Suisei ni Nareta nara.osz", + "1719687 EmoCosine - Love Kills U.osz", + "1733940 WHITEFISTS feat. Sennzai - Paralyzed Ash.osz", + "1734692 EmoCosine - Cutter.osz", + "1739529 luvlxckdown - tbh i dont like being social.osz", + "1756970 Kurubukko vs. yukitani - Minamichita EVOLVED.osz", + "1762209 Marmalade butcher - Immortality Math Club.osz", + "1765720 ZxNX - FORTALiCE.osz", + "1786165 NILFRUITS - Arandano.osz", + "1787258 SAMString - Night Fighter.osz", + "1791462 ZxNX - Schadenfreude.osz", + "1793821 Kobaryo - The Lightning Sword.osz", + "1796440 kuru x miraie - re:start.osz", + "1799285 Origami Angel - 666 Flags.osz", + "1812415 nanobii - Rainbow Road.osz", + "1814682 NIWASHI - Y.osz", + "1818361 meganeko - Feral (osu! edit).osz", + "1818924 fiend - Disconnect.osz", + "1838730 Pegboard Nerds - Disconnected.osz", + "1854710 Blaster & Extra Terra - Spacecraft (Cut Ver.).osz", + "1859322 Hino Isuka - Delightness Brightness.osz", + "1884102 Maduk - Go (feat. Lachi) (Cut Ver.).osz", + "1884578 Neko Hacker - People People feat. Nanahira.osz", + "1897902 uma vs. Morimori Atsushi - Re: End of a Dream.osz", + "1905582 KINEMA106 - Fly Away (Cut Ver.).osz", + "1934686 ARForest - Rainbow Magic!!.osz", + "1963076 METAROOM - S.N.U.F.F.Y.osz", + "1968973 Stars Hollow - Out the Sunroof..osz", + "1971951 James Landino - Shiba Paradise.osz", + "1972518 Toromaru - Sleight of Hand.osz", + "1982302 KINEMA106 - INVITE.osz", + "1983475 KNOWER - The Government Knows.osz", + "2010165 Junk - Yellow Smile (bms edit).osz", + "2022737 Andora - Euphoria (feat. WaMi).osz", + "2025023 tephe - Genjitsu Escape.osz", + "2052754 P4koo - 8th:Planet ~Re:search~.osz", + "2054122 Raimukun - Myths Orbis.osz", + "2121470 Raimukun - Nyarlathotep's Dreamland.osz", + "2122284 Agressor Bunx - Tornado (Cut Ver.).osz", + "2125034 Agressor Bunx - Acid Mirage (Cut Ver.).osz", + "2136263 Se-U-Ra - Cris Fortress.osz", }; private static readonly string[] bundled_catch = { - "554256 Helblinde - When Time Sleeps.osz", - "693123 yuki. - Nadeshiko Sensation.osz", - "767009 OISHII - PIZZA PLAZA.osz", - "767346 Thaehan - Bwa !.osz", - "815162 VINXIS - Greetings.osz", - "840964 cYsmix - Breeze.osz", - "932657 Wisp X - Eventide.osz", - "933700 onumi - CONFUSION PART ONE.osz", - "933984 onumi - PERSONALITY.osz", - "934785 onumi - FAKE.osz", - "936545 onumi - REGRET PART ONE.osz", - "943803 Fractal Dreamers - Everything for a Dream.osz", - "943876 S3RL - I Will Pick You Up (feat. Tamika).osz", - "946773 Trial & Error - DREAMING COLOR (Short Ver.).osz", - "955808 Trial & Error - Tokoyami no keiyaku KEGARETA-SHOUJO feat. GUMI (Short Ver.).osz", - "957808 Fractal Dreamers - Module_410.osz", - "957842 antiPLUR - One Life Left to Live.osz", - "965730 The Flashbulb - Lawn Wake IV (Black).osz", - "966240 Creo - Challenger.osz", - "968232 Rin - Lunatic set 15 ~ The Moon as Seen from the Shrine.osz", - "972302 VINXIS - A Centralized View.osz", - "972887 HyuN - Illusion of Inflict.osz", - "1008600 LukHash - WHEN AN ANGEL DIES.osz", - "1032103 LukHash - H8 U.osz", + @"693123 yuki. - Nadeshiko Sensation.osz", + @"833719 FOLiACETATE - Heterochromia Iridis.osz", + @"981762 siromaru + cranky - conflict.osz", + @"1008600 LukHash - WHEN AN ANGEL DIES.osz", + @"1071294 dark cat - pursuit of happiness.osz", + @"1102115 meganeko - Nova.osz", + @"1115500 Chopin - Etude Op. 25, No. 12 (meganeko Remix).osz", + @"1128274 LeaF - Wizdomiot.osz", + @"1141049 HyuN feat. JeeE - Fallen Angel.osz", + @"1148215 Zekk - Fluctuation.osz", + @"1151833 ginkiha - nightfall.osz", + @"1158124 PUP - Dark Days.osz", + @"1184890 IAHN - Transform (Original Mix).osz", + @"1195922 Disasterpeace - Home.osz", + @"1197461 MIMI - Nanimo nai Youna.osz", + @"1197924 Camellia feat. Nanahira - Looking For A New Adventure.osz", + @"1203594 ginkiha - Anemoi.osz", + @"1211572 MIMI - Lapis Lazuli.osz", + @"1231601 Lime - Harmony.osz", + @"1240162 P4koo - 8th:Planet ~Re:search~.osz", + @"1246000 Zekk - Calling.osz", + @"1249928 Thaehan - Yuujou.osz", + @"1258751 Umeboshi Chazuke - ICHIBANBOSHI*ROCKET.osz", + @"1264818 Umeboshi Chazuke - Panic! Pop'n! Picnic! (2019 REMASTER).osz", + @"1280183 IAHN - Mad Halloween.osz", + @"1303201 Umeboshi Chazuke - Run*2 Run To You!!.osz", + @"1328918 Kobaryo - Theme for Psychopath Justice.osz", + @"1338215 Lime - Renai Syndrome.osz", + @"1338796 uma vs. Morimori Atsushi - Re:End of a Dream.osz", + @"1340492 MYUKKE. - The 89's Momentum.osz", + @"1393933 Mastermind (xi+nora2r) - Dreadnought.osz", + @"1400205 m108 - XIII Charlotte.osz", + @"1471328 Lime - Chronomia.osz", + @"1503591 Origami Angel - The Title Track.osz", + @"1524173 litmus* as Ester - Krave.osz", + @"1541235 Getty vs. DJ DiA - Grayed Out -Antifront-.osz", + @"1554250 Shawn Wasabi - Otter Pop (feat. Hollis).osz", + @"1583461 Sound Souler - Absent Color.osz", + @"1638487 tokiwa - wasurena feat. Sennzai.osz", + @"1698949 ZxNX - Schadenfreude.osz", + @"1704324 xi - Time files.osz", + @"1756405 Fractal Dreamers - Kingdom of Silence.osz", + @"1769575 cYsmix - Peer Gynt.osz", + @"1770054 Ardolf - Split.osz", + @"1772648 in love with a ghost - interdimensional portal leading to a cute place feat. snail's house.osz", + @"1776379 in love with a ghost - i thought we were lovers w/ basil.osz", + @"1779476 URBANGARDE - KIMI WA OKUMAGASO.osz", + @"1789435 xi - Parousia.osz", + @"1794190 Se-U-Ra - The Endless for Traveler.osz", + @"1799889 Waterflame - Ricochet Love.osz", + @"1816401 Gram vs. Yooh - Apocalypse.osz", + @"1826327 -45 - Total Eclipse of The Sun.osz", + @"1830796 xi - Halcyon.osz", + @"1924231 Mili - Nine Point Eight.osz", + @"1952903 Cres. - End Time.osz", + @"1970946 Good Kid - Slingshot.osz", + @"1982063 linear ring - enchanted love.osz", + @"2000438 Toromaru - Erinyes.osz", + @"2124277 II-L - VANGUARD-3.osz", + @"2147529 Nashimoto Ui - AaAaAaAAaAaAAa (Cut Ver.).osz", }; private static readonly string[] bundled_mania = { - "943516 antiPLUR - Clockwork Spooks.osz", - "946394 VINXIS - Three Times The Original Charm.osz", - "966408 antiPLUR - One Life Left to Live.osz", - "971561 antiPLUR - Runengon.osz", - "983864 James Landino - Shiba Island.osz", - "989512 BilliumMoto - 1xMISS.osz", - "994104 James Landino - Reaction feat. Slyleaf.osz", - "1003217 nekodex - circles!.osz", - "1009907 James Landino & Kabuki - Birdsong.osz", - "1015169 Thaehan - Insert Coin.osz", + @"1008419 BilliumMoto - Four Veiled Stars.osz", + @"1025170 Frums - We Want To Run.osz", + @"1092856 F-777 - Viking Arena.osz", + @"1139247 O2i3 - Heart Function.osz", + @"1154007 LeaF - ATHAZA.osz", + @"1170054 Zekk - Fallen.osz", + @"1212132 Street - Koiyamai (TV Size).osz", + @"1226466 Se-U-Ra - Elif to Shiro Kura no Yoru -Called-.osz", + @"1247210 Frums - Credits.osz", + @"1254196 ARForest - Regret.osz", + @"1258829 Umeboshi Chazuke - Cineraria.osz", + @"1300398 ARForest - The Last Page.osz", + @"1305627 Frums - Star of the COME ON!!.osz", + @"1348806 Se-U-Ra - LOA2.osz", + @"1375449 yuki. - Nadeshiko Sensation.osz", + @"1448292 Cres. - End Time.osz", + @"1479741 Reku Mochizuki - FORViDDEN ENERZY -Fataldoze-.osz", + @"1494747 Fractal Dreamers - Whispers from a Distant Star.osz", + @"1505336 litmus* - Rush-More.osz", + @"1508963 ARForest - Rainbow Magic!!.osz", + @"1727126 Chroma - Strange Inventor.osz", + @"1737101 ZxNX - FORTALiCE.osz", + @"1740952 Sobrem x Silentroom - Random.osz", + @"1756251 Plum - Mad Piano Party.osz", + @"1909163 Frums - theyaremanycolors.osz", + @"1916285 siromaru + cranky - conflict.osz", + @"1948972 Ardolf - Split.osz", + @"1957138 GLORYHAMMER - Rise Of The Chaos Wizards.osz", + @"1972411 James Landino - Shiba Paradise.osz", + @"1978179 Andora - Flicker (feat. RANASOL).osz", + @"1987180 cygnus - The Evolution of War.osz", + @"1994458 tephe - Genjitsu Escape.osz", + @"1999339 Aice room - Nyan Nyan Dive (EmoCosine Remix).osz", + @"2015361 HoneyComeBear - Rainy Girl.osz", + @"2028108 HyuN - Infinity Heaven.osz", + @"2055329 miraie & blackwinterwells - facade.osz", + @"2069877 Sephid - Thunderstrike 1988.osz", + @"2119716 Aethoro - Snowy.osz", + @"2120379 Synthion - VIVIDVELOCITY.osz", + @"2124805 Frums (unknown ""lambda"") - 19ZZ.osz", + @"2127811 Wiklund - Joy of Living (Cut Ver.).osz", }; } } From 1d6915478f485998a47e6926bd3927dd533d1309 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 May 2024 14:26:49 +0800 Subject: [PATCH 413/581] Add message letting users know that beatmaps are donwloading in the background --- .../FirstRunSetupBeatmapScreenStrings.cs | 5 +++++ .../Overlays/FirstRunSetup/ScreenBeatmaps.cs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs index a77ee066e4..50a417312d 100644 --- a/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs +++ b/osu.Game/Localisation/FirstRunSetupBeatmapScreenStrings.cs @@ -39,6 +39,11 @@ namespace osu.Game.Localisation /// public static LocalisableString BundledButton => new TranslatableString(getKey(@"bundled_button"), @"Get recommended beatmaps"); + /// + /// "Beatmaps will be downloaded in the background. You can continue with setup while this happens!" + /// + public static LocalisableString DownloadingInBackground => new TranslatableString(getKey(@"downloading_in_background"), @"Beatmaps will be downloaded in the background. You can continue with setup while this happens!"); + /// /// "You can also obtain more beatmaps from the main menu "browse" button at any time." /// diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 385695f669..da60951ab6 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Localisation; using osu.Game.Online; using osuTK; +using osuTK.Graphics; using Realms; namespace osu.Game.Overlays.FirstRunSetup @@ -25,6 +26,8 @@ namespace osu.Game.Overlays.FirstRunSetup private ProgressRoundedButton downloadBundledButton = null!; private ProgressRoundedButton downloadTutorialButton = null!; + private OsuTextFlowContainer downloadInBackgroundText = null!; + private OsuTextFlowContainer currentlyLoadedBeatmaps = null!; private BundledBeatmapDownloader? tutorialDownloader; @@ -100,6 +103,15 @@ namespace osu.Game.Overlays.FirstRunSetup Text = FirstRunSetupBeatmapScreenStrings.BundledButton, Action = downloadBundled }, + downloadInBackgroundText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) + { + Colour = OverlayColourProvider.Light2, + Alpha = 0, + TextAnchor = Anchor.TopCentre, + Text = FirstRunSetupBeatmapScreenStrings.DownloadingInBackground, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, @@ -169,6 +181,10 @@ namespace osu.Game.Overlays.FirstRunSetup if (bundledDownloader != null) return; + downloadInBackgroundText + .FlashColour(Color4.White, 500) + .FadeIn(200); + bundledDownloader = new BundledBeatmapDownloader(false); AddInternal(bundledDownloader); From 0ddd3cbdc5cba5ccc60e72ed97ebe07629915249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 7 May 2024 17:02:25 +0800 Subject: [PATCH 414/581] Randomise which menu content is shown on arriving at the menu --- osu.Game/Screens/Menu/OnlineMenuBanner.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/OnlineMenuBanner.cs b/osu.Game/Screens/Menu/OnlineMenuBanner.cs index 6f98b73939..b9d269c82a 100644 --- a/osu.Game/Screens/Menu/OnlineMenuBanner.cs +++ b/osu.Game/Screens/Menu/OnlineMenuBanner.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -111,7 +112,9 @@ namespace osu.Game.Screens.Menu content.AddRange(loaded); - displayIndex = -1; + // Many users don't spend much time at the main menu, so let's randomise where in the + // carousel of available images we start at to give each a fair chance. + displayIndex = RNG.Next(0, images.NewValue.Images.Length) - 1; showNext(); }, (cancellationTokenSource ??= new CancellationTokenSource()).Token); } From ab2677d913552b5d381c91675d8bf6628853585e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Wolfschl=C3=A4ger?= Date: Tue, 7 May 2024 17:15:49 +0200 Subject: [PATCH 415/581] Changed default delete confirmation content strings for better translatability --- .../DeleteConfirmationContentStrings.cs | 24 +++++++++---------- .../DeleteConfirmationDialogStrings.cs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs index d9e90675f7..563fbf5654 100644 --- a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs +++ b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs @@ -10,34 +10,34 @@ namespace osu.Game.Localisation private const string prefix = @"osu.Game.Resources.Localisation.DeleteConfirmationContent"; /// - /// "All beatmaps?" + /// "Are you sure you want to delete all beatmaps?" /// - public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"All beatmaps?"); + public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"Are you sure you want to delete all beatmaps?"); /// - /// "All beatmaps videos? This cannot be undone!" + /// "Are you sure you want to delete all beatmaps videos? This cannot be undone!" /// - public static LocalisableString BeatmapVideos => new TranslatableString(getKey(@"beatmap_videos"), @"All beatmaps videos? This cannot be undone!"); + public static LocalisableString BeatmapVideos => new TranslatableString(getKey(@"beatmap_videos"), @"Are you sure you want to delete all beatmaps videos? This cannot be undone!"); /// - /// "All skins? This cannot be undone!" + /// "Are you sure you want to delete all skins? This cannot be undone!" /// - public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"All skins? This cannot be undone!"); + public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"Are you sure you want to delete all skins? This cannot be undone!"); /// - /// "All collections? This cannot be undone!" + /// "Are you sure you want to delete all collections? This cannot be undone!" /// - public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"All collections? This cannot be undone!"); + public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Are you sure you want to delete all collections? This cannot be undone!"); /// - /// "All scores? This cannot be undone!" + /// "Are you sure you want to delete all scores? This cannot be undone!" /// - public static LocalisableString Scores => new TranslatableString(getKey(@"collections"), @"All scores? This cannot be undone!"); + public static LocalisableString Scores => new TranslatableString(getKey(@"collections"), @"Are you sure you want to delete all scores? This cannot be undone!"); /// - /// "All mod presets?" + /// "Are you sure you want to delete all mod presets?" /// - public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"All mod presets?"); + public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Are you sure you want to delete all mod presets?"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Localisation/DeleteConfirmationDialogStrings.cs b/osu.Game/Localisation/DeleteConfirmationDialogStrings.cs index 33738fe95e..25997eadd3 100644 --- a/osu.Game/Localisation/DeleteConfirmationDialogStrings.cs +++ b/osu.Game/Localisation/DeleteConfirmationDialogStrings.cs @@ -10,9 +10,9 @@ namespace osu.Game.Localisation private const string prefix = @"osu.Game.Resources.Localisation.DeleteConfirmationDialog"; /// - /// "Confirm deletion of" + /// "Caution" /// - public static LocalisableString HeaderText => new TranslatableString(getKey(@"header_text"), @"Confirm deletion of"); + public static LocalisableString HeaderText => new TranslatableString(getKey(@"header_text"), @"Caution"); /// /// "Yes. Go for it." From 7551cf01d18a4e924ae9720417fa1797e8b38db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 8 May 2024 09:45:52 +0200 Subject: [PATCH 416/581] Fix test failure --- osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index cc7b37e6a8..8ca141bb4f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -14,7 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Legacy; using osu.Game.IO.Legacy; -using osu.Game.Models; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; @@ -226,10 +226,10 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } }; scoreInfo.OnlineID = 123123; - scoreInfo.RealmUser = new RealmUser + scoreInfo.User = new APIUser { Username = "spaceman_atlas", - OnlineID = 3035836, + Id = 3035836, CountryCode = CountryCode.PL }; scoreInfo.ClientVersion = "2023.1221.0"; From 88b20b357ae0d6ac5d1549981ec07e9a3f731cee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 May 2024 14:38:10 +0800 Subject: [PATCH 417/581] Increase padding around text in dialogs --- osu.Game/Overlays/Dialog/PopupDialog.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 4ac37a63e2..a23c394c9f 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -210,7 +210,7 @@ namespace osu.Game.Overlays.Dialog RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, TextAnchor = Anchor.TopCentre, - Padding = new MarginPadding { Horizontal = 5 }, + Padding = new MarginPadding { Horizontal = 15 }, }, body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18)) { @@ -219,7 +219,7 @@ namespace osu.Game.Overlays.Dialog TextAnchor = Anchor.TopCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 5 }, + Padding = new MarginPadding { Horizontal = 15 }, }, buttonsContainer = new FillFlowContainer { From 3b8b56cbcbdb0365a03231aa151efb0050c12ebf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 9 May 2024 20:18:53 +0900 Subject: [PATCH 418/581] Apply required changes after framework masking updates --- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 +-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 +-- osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 +-- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 +- .../Edit/Compose/Components/Timeline/TimelineTickDisplay.cs | 3 +-- 7 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 836d348ff4..afc2d407e9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } // Generally all the control points are within the visible area all the time. - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => true; + public override bool UpdateSubTreeMasking() => true; /// /// Handles correction of invalid path types. diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 4933eb4041..93c3450904 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -8,7 +8,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -37,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.UI // For osu! gameplay, everything is always on screen. // Skipping masking calculations improves performance in intense beatmaps (ie. https://osu.ppy.sh/beatmapsets/150945#osu/372245) - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false; + public override bool UpdateSubTreeMasking() => false; public SmokeContainer Smoke { get; } public FollowPointRenderer FollowPoints { get; } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 0510f08068..bdcb341fb4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Judgements; @@ -345,7 +344,7 @@ namespace osu.Game.Rulesets.Taiko.UI { public void Add(Drawable proxy) => AddInternal(proxy); - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) + public override bool UpdateSubTreeMasking() { // DrawableHitObject disables masking. // Hitobject content is proxied and unproxied based on hit status and the IsMaskedAway value could get stuck because of this. diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs index 3a2db4fc71..111dede815 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false; + public override bool UpdateSubTreeMasking() => false; protected override void OnDrag(DragEvent e) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index de05219212..3ce6cc3cef 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -15,7 +15,6 @@ using osu.Framework.Extensions.ListExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Primitives; using osu.Framework.Lists; using osu.Framework.Threading; using osu.Framework.Utils; @@ -632,7 +631,7 @@ namespace osu.Game.Rulesets.Objects.Drawables #endregion - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false; + public override bool UpdateSubTreeMasking() => false; protected override void UpdateAfterChildren() { diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index b49924762e..c4feb249f4 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.UI break; base.UpdateSubTree(); - UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); + UpdateSubTreeMasking(); } while (state == PlaybackState.RequiresCatchUp && stopwatch.ElapsedMilliseconds < max_catchup_milliseconds); return true; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index c3adb43032..e16c8519e5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Graphics; -using osu.Framework.Graphics.Primitives; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -20,7 +19,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public partial class TimelineTickDisplay : TimelinePart { // With current implementation every tick in the sub-tree should be visible, no need to check whether they are masked away. - public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false; + public override bool UpdateSubTreeMasking() => false; [Resolved] private EditorBeatmap beatmap { get; set; } = null!; From c4d6318c0d0026bb5b581fb198aded74376de775 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 May 2024 22:12:09 +0800 Subject: [PATCH 419/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 97dfe5d9f7..e20ac2e0b7 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 66347acdf0..103ef50e0c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From de05998421b2c04d669bf08897e6fd82e49d1815 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 9 May 2024 22:17:00 +0800 Subject: [PATCH 420/581] Avoid weird codestyle rules --- osu.Desktop/Program.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index d8364fc6e6..23e56cdce9 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -107,11 +107,13 @@ namespace osu.Desktop } } - using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions - { - IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null, - FriendlyGameName = OsuGameBase.GAME_NAME, - })) + var hostOptions = new HostOptions + { + IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null, + FriendlyGameName = OsuGameBase.GAME_NAME, + }; + + using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, hostOptions)) { if (!host.IsPrimaryInstance) { From 6b91b4abf41660fb030f2d593df28159442cf145 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 8 May 2024 03:58:10 +0300 Subject: [PATCH 421/581] Add simple implementation for extended mods display in new footer design --- .../TestSceneFooterButtonModsV2.cs | 120 +++++++++ osu.Game/Screens/Play/HUD/ModDisplay.cs | 7 +- .../Select/FooterV2/FooterButtonModsV2.cs | 249 +++++++++++++++++- .../Screens/Select/FooterV2/FooterButtonV2.cs | 126 ++++----- 4 files changed, 436 insertions(+), 66 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs new file mode 100644 index 0000000000..7e8bba6573 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs @@ -0,0 +1,120 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Screens.Select.FooterV2; +using osu.Game.Utils; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public partial class TestSceneFooterButtonModsV2 : OsuTestScene + { + private readonly TestFooterButtonModsV2 footerButtonMods; + + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + + public TestSceneFooterButtonModsV2() + { + Add(footerButtonMods = new TestFooterButtonModsV2 + { + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + X = -100, + Action = () => { }, + }); + } + + [Test] + public void TestDisplay() + { + AddStep("one mod", () => changeMods(new List { new OsuModHidden() })); + AddStep("two mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock() })); + AddStep("three mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() })); + AddStep("four mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() })); + AddStep("five mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() })); + + AddStep("clear mods", () => changeMods(Array.Empty())); + AddWaitStep("wait", 3); + AddStep("one mod", () => changeMods(new List { new OsuModHidden() })); + + AddStep("clear mods", () => changeMods(Array.Empty())); + AddWaitStep("wait", 3); + AddStep("five mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() })); + } + + [Test] + public void TestIncrementMultiplier() + { + var hiddenMod = new Mod[] { new OsuModHidden() }; + AddStep(@"Add Hidden", () => changeMods(hiddenMod)); + AddAssert(@"Check Hidden multiplier", () => assertModsMultiplier(hiddenMod)); + + var hardRockMod = new Mod[] { new OsuModHardRock() }; + AddStep(@"Add HardRock", () => changeMods(hardRockMod)); + AddAssert(@"Check HardRock multiplier", () => assertModsMultiplier(hardRockMod)); + + var doubleTimeMod = new Mod[] { new OsuModDoubleTime() }; + AddStep(@"Add DoubleTime", () => changeMods(doubleTimeMod)); + AddAssert(@"Check DoubleTime multiplier", () => assertModsMultiplier(doubleTimeMod)); + + var multipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() }; + AddStep(@"Add multiple Mods", () => changeMods(multipleIncrementMods)); + AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(multipleIncrementMods)); + } + + [Test] + public void TestDecrementMultiplier() + { + var easyMod = new Mod[] { new OsuModEasy() }; + AddStep(@"Add Easy", () => changeMods(easyMod)); + AddAssert(@"Check Easy multiplier", () => assertModsMultiplier(easyMod)); + + var noFailMod = new Mod[] { new OsuModNoFail() }; + AddStep(@"Add NoFail", () => changeMods(noFailMod)); + AddAssert(@"Check NoFail multiplier", () => assertModsMultiplier(noFailMod)); + + var multipleDecrementMods = new Mod[] { new OsuModEasy(), new OsuModNoFail() }; + AddStep(@"Add Multiple Mods", () => changeMods(multipleDecrementMods)); + AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(multipleDecrementMods)); + } + + [Test] + public void TestUnrankedBadge() + { + AddStep(@"Add unranked mod", () => changeMods(new[] { new OsuModDeflate() })); + AddUntilStep("Unranked badge shown", () => footerButtonMods.UnrankedBadge.Alpha == 1); + AddStep(@"Clear selected mod", () => changeMods(Array.Empty())); + AddUntilStep("Unranked badge not shown", () => footerButtonMods.UnrankedBadge.Alpha == 0); + } + + private void changeMods(IReadOnlyList mods) + { + footerButtonMods.Current.Value = mods; + } + + private bool assertModsMultiplier(IEnumerable mods) + { + double multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier); + string expectedValue = ModUtils.FormatScoreMultiplier(multiplier).ToString(); + + return expectedValue == footerButtonMods.MultiplierText.Current.Value; + } + + private partial class TestFooterButtonModsV2 : FooterButtonModsV2 + { + public new Container UnrankedBadge => base.UnrankedBadge; + public new OsuSpriteText MultiplierText => base.MultiplierText; + } + } +} diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index ba948b516e..75db720603 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -20,6 +20,7 @@ namespace osu.Game.Screens.Play.HUD /// public partial class ModDisplay : CompositeDrawable, IHasCurrentValue> { + private readonly bool showExtendedInformation; private const int fade_duration = 1000; public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; @@ -39,8 +40,10 @@ namespace osu.Game.Screens.Play.HUD private readonly FillFlowContainer iconsContainer; - public ModDisplay() + public ModDisplay(bool showExtendedInformation = true) { + this.showExtendedInformation = showExtendedInformation; + AutoSizeAxes = Axes.Both; InternalChild = iconsContainer = new ReverseChildIDFillFlowContainer @@ -64,7 +67,7 @@ namespace osu.Game.Screens.Play.HUD iconsContainer.Clear(); foreach (Mod mod in mods.NewValue.AsOrdered()) - iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) }); + iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(0.6f) }); appearTransform(); } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index b8c9f0b34b..f9d923ddcf 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -1,20 +1,261 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Overlays; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play.HUD; +using osu.Game.Utils; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Select.FooterV2 { - public partial class FooterButtonModsV2 : FooterButtonV2 + public partial class FooterButtonModsV2 : FooterButtonV2, IHasCurrentValue> { - [BackgroundDependencyLoader] - private void load(OsuColour colour) + // todo: see https://github.com/ppy/osu-framework/issues/3271 + private const float torus_scale_factor = 1.2f; + + private readonly BindableWithCurrent> current = new BindableWithCurrent>(Array.Empty()); + + public Bindable> Current { + get => current.Current; + set => current.Current = value; + } + + private Container modDisplayBar = null!; + + protected Container UnrankedBadge { get; private set; } = null!; + + private ModDisplay modDisplay = null!; + private OsuSpriteText modCountText = null!; + + protected OsuSpriteText MultiplierText { get; private set; } = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + const float bar_shear_width = 7f; + const float bar_height = 37f; + const float display_rel_width = 0.65f; + + var barShear = new Vector2(bar_shear_width / bar_height, 0); + Text = "Mods"; Icon = FontAwesome.Solid.ExchangeAlt; - AccentColour = colour.Lime1; + AccentColour = colours.Lime1; + + AddRange(new[] + { + UnrankedBadge = new Container + { + Position = new Vector2(BUTTON_WIDTH + 5f, -5f), + Depth = float.MaxValue, + Origin = Anchor.BottomLeft, + Shear = barShear, + CornerRadius = CORNER_RADIUS, + AutoSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2f, + Children = new Drawable[] + { + new Box + { + Colour = colours.Red2, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Shear = -barShear, + Text = ModSelectOverlayStrings.Unranked.ToUpper(), + Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), + Colour = Color4.Black, + } + } + }, + modDisplayBar = new Container + { + Y = -5f, + Depth = float.MaxValue, + Origin = Anchor.BottomLeft, + Shear = barShear, + CornerRadius = CORNER_RADIUS, + Size = new Vector2(BUTTON_WIDTH, bar_height), + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 4, + // Figma says 50% opacity, but it does not match up visually if taken at face value, and looks bad. + Colour = Colour4.Black.Opacity(0.25f), + Offset = new Vector2(0, 2), + }, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + new Container + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + Width = 1f - display_rel_width, + Masking = true, + Child = MultiplierText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = -barShear, + Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold) + } + }, + new Container + { + CornerRadius = CORNER_RADIUS, + RelativeSizeAxes = Axes.Both, + Width = display_rel_width, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background3, + RelativeSizeAxes = Axes.Both, + }, + modDisplay = new ModDisplay(showExtendedInformation: false) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = -barShear, + Scale = new Vector2(0.6f), + Current = { Value = new List { new ModCinema() } }, + ExpansionMode = ExpansionMode.AlwaysContracted, + }, + modCountText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = -barShear, + Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), + } + } + }, + } + }, + }); + } + + private ModSettingChangeTracker? modSettingChangeTracker; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(m => + { + modSettingChangeTracker?.Dispose(); + + updateDisplay(); + + if (m.NewValue != null) + { + modSettingChangeTracker = new ModSettingChangeTracker(m.NewValue); + modSettingChangeTracker.SettingChanged += _ => updateDisplay(); + } + }, true); + + FinishTransforms(true); + } + + private const double duration = 240; + private const Easing easing = Easing.OutQuint; + + private void updateDisplay() + { + if (Current.Value.Count == 0) + { + modDisplayBar.MoveToY(20, duration, easing); + modDisplayBar.FadeOut(duration, easing); + modDisplay.FadeOut(duration, easing); + modCountText.FadeOut(duration, easing); + + UnrankedBadge.MoveToY(20, duration, easing); + UnrankedBadge.FadeOut(duration, easing); + + // add delay to let unranked indicator hide first before resizing the button back to its original width. + this.Delay(duration).ResizeWidthTo(BUTTON_WIDTH, duration, easing); + } + else + { + if (Current.Value.Count >= 5) + { + modCountText.Text = $"{Current.Value.Count} MODS"; + modCountText.FadeIn(duration, easing); + modDisplay.FadeOut(duration, easing); + } + else + { + modDisplay.Current.Value = Current.Value; + modDisplay.FadeIn(duration, easing); + modCountText.FadeOut(duration, easing); + } + + if (Current.Value.Any(m => !m.Ranked)) + { + UnrankedBadge.MoveToX(BUTTON_WIDTH + 5, duration, easing); + UnrankedBadge.FadeIn(duration, easing); + + this.ResizeWidthTo(BUTTON_WIDTH + UnrankedBadge.DrawWidth + 10, duration, easing); + } + else + { + UnrankedBadge.MoveToX(BUTTON_WIDTH + 5 - UnrankedBadge.DrawWidth, duration, easing); + UnrankedBadge.FadeOut(duration, easing); + + this.ResizeWidthTo(BUTTON_WIDTH, duration, easing); + } + + modDisplayBar.MoveToY(-5, duration, Easing.OutQuint); + UnrankedBadge.MoveToY(-5, duration, easing); + modDisplayBar.FadeIn(duration, easing); + } + + double multiplier = Current.Value?.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier) ?? 1; + MultiplierText.Text = ModUtils.FormatScoreMultiplier(multiplier); + + if (multiplier > 1) + MultiplierText.FadeColour(colours.Red1, duration, easing); + else if (multiplier < 1) + MultiplierText.FadeColour(colours.Lime1, duration, easing); + else + MultiplierText.FadeColour(Color4.White, duration, easing); } } } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs index 2f5046d2bb..a7bd1b8abd 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs @@ -24,17 +24,18 @@ namespace osu.Game.Screens.Select.FooterV2 { public partial class FooterButtonV2 : OsuClickableContainer, IKeyBindingHandler { - private const int button_height = 90; - private const int button_width = 140; - private const int corner_radius = 10; private const int transition_length = 500; // This should be 12 by design, but an extra allowance is added due to the corner radius specification. public const float SHEAR_WIDTH = 13.5f; + public const int CORNER_RADIUS = 10; + public const int BUTTON_HEIGHT = 90; + public const int BUTTON_WIDTH = 140; + public Bindable OverlayState = new Bindable(); - protected static readonly Vector2 SHEAR = new Vector2(SHEAR_WIDTH / button_height, 0); + protected static readonly Vector2 BUTTON_SHEAR = new Vector2(SHEAR_WIDTH / BUTTON_HEIGHT, 0); [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -70,68 +71,73 @@ namespace osu.Game.Screens.Select.FooterV2 public FooterButtonV2() { - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = 4, - // Figma says 50% opacity, but it does not match up visually if taken at face value, and looks bad. - Colour = Colour4.Black.Opacity(0.25f), - Offset = new Vector2(0, 2), - }; - Shear = SHEAR; - Size = new Vector2(button_width, button_height); - Masking = true; - CornerRadius = corner_radius; - Children = new Drawable[] - { - backgroundBox = new Box - { - RelativeSizeAxes = Axes.Both - }, + Size = new Vector2(BUTTON_WIDTH, BUTTON_HEIGHT); + Margin = new MarginPadding { Horizontal = SHEAR_WIDTH / 2f }; - // For elements that should not be sheared. - new Container + Child = new Container + { + EdgeEffect = new EdgeEffectParameters { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Shear = -SHEAR, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - TextContainer = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Y = 42, - AutoSizeAxes = Axes.Both, - Child = text = new OsuSpriteText - { - // figma design says the size is 16, but due to the issues with font sizes 19 matches better - Font = OsuFont.TorusAlternate.With(size: 19), - AlwaysPresent = true - } - }, - icon = new SpriteIcon - { - Y = 12, - Size = new Vector2(20), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - } + Type = EdgeEffectType.Shadow, + Radius = 4, + // Figma says 50% opacity, but it does not match up visually if taken at face value, and looks bad. + Colour = Colour4.Black.Opacity(0.25f), + Offset = new Vector2(0, 2), }, - new Container + Shear = BUTTON_SHEAR, + Masking = true, + CornerRadius = CORNER_RADIUS, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Shear = -SHEAR, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Y = -corner_radius, - Size = new Vector2(120, 6), - Masking = true, - CornerRadius = 3, - Child = bar = new Box + backgroundBox = new Box { + RelativeSizeAxes = Axes.Both + }, + // For elements that should not be sheared. + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Shear = -BUTTON_SHEAR, RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + TextContainer = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 42, + AutoSizeAxes = Axes.Both, + Child = text = new OsuSpriteText + { + // figma design says the size is 16, but due to the issues with font sizes 19 matches better + Font = OsuFont.TorusAlternate.With(size: 19), + AlwaysPresent = true + } + }, + icon = new SpriteIcon + { + Y = 12, + Size = new Vector2(20), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + } + }, + new Container + { + Shear = -BUTTON_SHEAR, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Y = -CORNER_RADIUS, + Size = new Vector2(120, 6), + Masking = true, + CornerRadius = 3, + Child = bar = new Box + { + RelativeSizeAxes = Axes.Both, + } } } }; From d7b658ec76dff55ec15cd2751861fe04ef414668 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 02:21:06 +0300 Subject: [PATCH 422/581] Add similar test steps to song select footer test scene --- .../SongSelect/TestSceneSongSelectFooterV2.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs index 013bad55bc..0c7725db5a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.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. +using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -10,7 +12,9 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.FooterV2; using osuTK.Input; @@ -47,7 +51,7 @@ namespace osu.Game.Tests.Visual.SongSelect overlay = new DummyOverlay() }; - footer.AddButton(modsButton = new FooterButtonModsV2(), overlay); + footer.AddButton(modsButton = new FooterButtonModsV2 { Current = SelectedMods }, overlay); footer.AddButton(randomButton = new FooterButtonRandomV2 { NextRandom = () => nextRandomCalled = true, @@ -61,9 +65,28 @@ namespace osu.Game.Tests.Visual.SongSelect [SetUpSteps] public void SetUpSteps() { + AddStep("clear mods", () => SelectedMods.Value = Array.Empty()); AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo))); } + [Test] + public void TestMods() + { + AddStep("one mod", () => SelectedMods.Value = new List { new OsuModHidden() }); + AddStep("two mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock() }); + AddStep("three mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() }); + AddStep("four mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() }); + AddStep("five mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() }); + + AddStep("clear mods", () => SelectedMods.Value = Array.Empty()); + AddWaitStep("wait", 3); + AddStep("one mod", () => SelectedMods.Value = new List { new OsuModHidden() }); + + AddStep("clear mods", () => SelectedMods.Value = Array.Empty()); + AddWaitStep("wait", 3); + AddStep("five mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() }); + } + [Test] public void TestShowOptions() { From 49692e168eb8a7a72b93a6bb40f83f12575c4976 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 02:21:24 +0300 Subject: [PATCH 423/581] Fix `ModDisplay` expanding on load with "always contracted/expanded" modes This is especially visible when reloading `SongSelectFooterV2` while multiple mods are already selected. The mods will appear expanded then contract. --- osu.Game/Screens/Play/HUD/ModDisplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 75db720603..f8c8232d45 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -60,6 +60,9 @@ namespace osu.Game.Screens.Play.HUD Current.BindValueChanged(updateDisplay, true); iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint); + + if (ExpansionMode == ExpansionMode.AlwaysExpanded || ExpansionMode == ExpansionMode.AlwaysContracted) + FinishTransforms(true); } private void updateDisplay(ValueChangedEvent> mods) From bff34a1c04f8151c053a1c32b31043b26f4e520c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 06:38:48 +0300 Subject: [PATCH 424/581] Add localisation and tooltip for mods count text --- .../SongSelect/TestSceneSongSelectFooterV2.cs | 6 ++ .../TestSceneFooterButtonModsV2.cs | 4 ++ .../Localisation/FooterButtonModsV2Strings.cs | 19 ++++++ .../Select/FooterV2/FooterButtonModsV2.cs | 62 +++++++++++++++++-- 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Localisation/FooterButtonModsV2Strings.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs index 0c7725db5a..93402e42ce 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs @@ -78,6 +78,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("four mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() }); AddStep("five mods", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() }); + AddStep("modified", () => SelectedMods.Value = new List { new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } }); + AddStep("modified + one", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } }); + AddStep("modified + two", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } }); + AddStep("modified + three", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } }); + AddStep("modified + four", () => SelectedMods.Value = new List { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDifficultyAdjust(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } }); + AddStep("clear mods", () => SelectedMods.Value = Array.Empty()); AddWaitStep("wait", 3); AddStep("one mod", () => SelectedMods.Value = new List { new OsuModHidden() }); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs index 7e8bba6573..af2eea6062 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs @@ -44,6 +44,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("four mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() })); AddStep("five mods", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() })); + AddStep("modified", () => changeMods(new List { new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } })); + AddStep("modified + one", () => changeMods(new List { new OsuModHidden(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } })); + AddStep("modified + two", () => changeMods(new List { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } })); + AddStep("clear mods", () => changeMods(Array.Empty())); AddWaitStep("wait", 3); AddStep("one mod", () => changeMods(new List { new OsuModHidden() })); diff --git a/osu.Game/Localisation/FooterButtonModsV2Strings.cs b/osu.Game/Localisation/FooterButtonModsV2Strings.cs new file mode 100644 index 0000000000..2cb297d8ef --- /dev/null +++ b/osu.Game/Localisation/FooterButtonModsV2Strings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class FooterButtonModsV2Strings + { + private const string prefix = @"osu.Game.Resources.Localisation.FooterButtonModsV2"; + + /// + /// "{0} mods" + /// + public static LocalisableString Mods(int count) => new TranslatableString(getKey(@"mods"), @"{0} mods", count); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index f9d923ddcf..7beadd1576 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -9,12 +9,14 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; @@ -155,15 +157,16 @@ namespace osu.Game.Screens.Select.FooterV2 Origin = Anchor.Centre, Shear = -barShear, Scale = new Vector2(0.6f), - Current = { Value = new List { new ModCinema() } }, + Current = { BindTarget = Current }, ExpansionMode = ExpansionMode.AlwaysContracted, }, - modCountText = new OsuSpriteText + modCountText = new ModCountText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Shear = -barShear, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), + Mods = { BindTarget = Current }, } } }, @@ -216,13 +219,11 @@ namespace osu.Game.Screens.Select.FooterV2 { if (Current.Value.Count >= 5) { - modCountText.Text = $"{Current.Value.Count} MODS"; modCountText.FadeIn(duration, easing); modDisplay.FadeOut(duration, easing); } else { - modDisplay.Current.Value = Current.Value; modDisplay.FadeIn(duration, easing); modCountText.FadeOut(duration, easing); } @@ -257,5 +258,58 @@ namespace osu.Game.Screens.Select.FooterV2 else MultiplierText.FadeColour(Color4.White, duration, easing); } + + private partial class ModCountText : OsuSpriteText, IHasCustomTooltip> + { + public readonly Bindable> Mods = new Bindable>(); + + protected override void LoadComplete() + { + base.LoadComplete(); + Mods.BindValueChanged(v => Text = FooterButtonModsV2Strings.Mods(v.NewValue.Count).ToUpper(), true); + } + + public ITooltip> GetCustomTooltip() => new ModTooltip(); + + public IReadOnlyList? TooltipContent => Mods.Value; + + public partial class ModTooltip : VisibilityContainer, ITooltip> + { + private ModDisplay extendedModDisplay = null!; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + AutoSizeAxes = Axes.Both; + CornerRadius = CORNER_RADIUS; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + extendedModDisplay = new ModDisplay + { + Margin = new MarginPadding { Vertical = 2f, Horizontal = 10f }, + Scale = new Vector2(0.6f), + ExpansionMode = ExpansionMode.AlwaysExpanded, + }, + }; + } + + public void SetContent(IReadOnlyList content) + { + extendedModDisplay.Current.Value = content; + } + + public void Move(Vector2 pos) => Position = pos; + + protected override void PopIn() => this.FadeIn(240, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(240, Easing.OutQuint); + } + } } } From c1ea9d2c9fd5c63f1c43830870e74a0bb090139a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 06:39:06 +0300 Subject: [PATCH 425/581] Adjust unranked badge height --- osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index 7beadd1576..ba4abc4025 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -78,7 +78,8 @@ namespace osu.Game.Screens.Select.FooterV2 Origin = Anchor.BottomLeft, Shear = barShear, CornerRadius = CORNER_RADIUS, - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + Height = bar_height, Masking = true, BorderColour = Color4.White, BorderThickness = 2f, @@ -91,9 +92,12 @@ namespace osu.Game.Screens.Select.FooterV2 }, new OsuSpriteText { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Shear = -barShear, Text = ModSelectOverlayStrings.Unranked.ToUpper(), - Margin = new MarginPadding { Horizontal = 15, Vertical = 5 }, + Margin = new MarginPadding { Horizontal = 15 }, + UseFullGlyphHeight = false, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), Colour = Color4.Black, } @@ -135,6 +139,7 @@ namespace osu.Game.Screens.Select.FooterV2 Anchor = Anchor.Centre, Origin = Anchor.Centre, Shear = -barShear, + UseFullGlyphHeight = false, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold) } }, From 6ddf8f849805e203fbfb0469cce923c0e9af2f68 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 06:47:48 +0300 Subject: [PATCH 426/581] Few cleanups --- osu.Game/Screens/Play/HUD/ModDisplay.cs | 2 +- .../Screens/Select/FooterV2/FooterButtonModsV2.cs | 7 +++---- osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs | 11 +++++------ osu.Game/Screens/Select/FooterV2/FooterV2.cs | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index f8c8232d45..b37d41e7a2 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -20,7 +20,6 @@ namespace osu.Game.Screens.Play.HUD /// public partial class ModDisplay : CompositeDrawable, IHasCurrentValue> { - private readonly bool showExtendedInformation; private const int fade_duration = 1000; public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; @@ -38,6 +37,7 @@ namespace osu.Game.Screens.Play.HUD } } + private readonly bool showExtendedInformation; private readonly FillFlowContainer iconsContainer; public ModDisplay(bool showExtendedInformation = true) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index ba4abc4025..bb259898ea 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; @@ -61,7 +60,7 @@ namespace osu.Game.Screens.Select.FooterV2 { const float bar_shear_width = 7f; const float bar_height = 37f; - const float display_rel_width = 0.65f; + const float mod_display_portion = 0.65f; var barShear = new Vector2(bar_shear_width / bar_height, 0); @@ -132,7 +131,7 @@ namespace osu.Game.Screens.Select.FooterV2 Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, - Width = 1f - display_rel_width, + Width = 1f - mod_display_portion, Masking = true, Child = MultiplierText = new OsuSpriteText { @@ -147,7 +146,7 @@ namespace osu.Game.Screens.Select.FooterV2 { CornerRadius = CORNER_RADIUS, RelativeSizeAxes = Axes.Both, - Width = display_rel_width, + Width = mod_display_portion, Masking = true, Children = new Drawable[] { diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs index a7bd1b8abd..2c841f6ae6 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs @@ -27,15 +27,15 @@ namespace osu.Game.Screens.Select.FooterV2 private const int transition_length = 500; // This should be 12 by design, but an extra allowance is added due to the corner radius specification. - public const float SHEAR_WIDTH = 13.5f; + private const float shear_width = 13.5f; - public const int CORNER_RADIUS = 10; - public const int BUTTON_HEIGHT = 90; - public const int BUTTON_WIDTH = 140; + protected const int CORNER_RADIUS = 10; + protected const int BUTTON_HEIGHT = 90; + protected const int BUTTON_WIDTH = 140; public Bindable OverlayState = new Bindable(); - protected static readonly Vector2 BUTTON_SHEAR = new Vector2(SHEAR_WIDTH / BUTTON_HEIGHT, 0); + protected static readonly Vector2 BUTTON_SHEAR = new Vector2(shear_width / BUTTON_HEIGHT, 0); [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -72,7 +72,6 @@ namespace osu.Game.Screens.Select.FooterV2 public FooterButtonV2() { Size = new Vector2(BUTTON_WIDTH, BUTTON_HEIGHT); - Margin = new MarginPadding { Horizontal = SHEAR_WIDTH / 2f }; Child = new Container { diff --git a/osu.Game/Screens/Select/FooterV2/FooterV2.cs b/osu.Game/Screens/Select/FooterV2/FooterV2.cs index 0529f0d082..370c28e2a5 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterV2.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Select.FooterV2 Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Direction = FillDirection.Horizontal, - Spacing = new Vector2(-FooterButtonV2.SHEAR_WIDTH + 7, 0), + Spacing = new Vector2(7, 0), AutoSizeAxes = Axes.Both } }; From e5b2023155006c15ecb7673d4395c612e400bce3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 07:05:32 +0300 Subject: [PATCH 427/581] Remove unappealing fade transition between mod display and count text Fading out the mod display looks quite ugly, since alpha is applied per mod sprite, introducing bad visual on the intersection between mods. --- .../Screens/Select/FooterV2/FooterButtonModsV2.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index bb259898ea..ed558d513b 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -221,16 +221,13 @@ namespace osu.Game.Screens.Select.FooterV2 } else { + modDisplay.Hide(); + modCountText.Hide(); + if (Current.Value.Count >= 5) - { - modCountText.FadeIn(duration, easing); - modDisplay.FadeOut(duration, easing); - } + modCountText.Show(); else - { - modDisplay.FadeIn(duration, easing); - modCountText.FadeOut(duration, easing); - } + modDisplay.Show(); if (Current.Value.Any(m => !m.Ranked)) { From f5ab9a7ff863a2f32870b6d517448b4c8a4b18c6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 07:12:28 +0300 Subject: [PATCH 428/581] Make unranked indicator orange to match mod select overlay --- osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index ed558d513b..3937f1e42a 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Select.FooterV2 { new Box { - Colour = colours.Red2, + Colour = colours.Orange2, RelativeSizeAxes = Axes.Both, }, new OsuSpriteText From 0392f7b04cfb913f5fe23ddc0da3726ffcbb7d80 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 07:36:59 +0300 Subject: [PATCH 429/581] Add tooltip to unranked indicator --- osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index 3937f1e42a..08b0407a79 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -70,7 +71,7 @@ namespace osu.Game.Screens.Select.FooterV2 AddRange(new[] { - UnrankedBadge = new Container + UnrankedBadge = new ContainerWithTooltip { Position = new Vector2(BUTTON_WIDTH + 5f, -5f), Depth = float.MaxValue, @@ -82,6 +83,7 @@ namespace osu.Game.Screens.Select.FooterV2 Masking = true, BorderColour = Color4.White, BorderThickness = 2f, + TooltipText = ModSelectOverlayStrings.UnrankedExplanation, Children = new Drawable[] { new Box @@ -312,5 +314,10 @@ namespace osu.Game.Screens.Select.FooterV2 protected override void PopOut() => this.FadeOut(240, Easing.OutQuint); } } + + private partial class ContainerWithTooltip : Container, IHasTooltip + { + public LocalisableString TooltipText { get; set; } + } } } From b6d6a8940bfb15313b8787185f45463391a79f1f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 08:24:39 +0300 Subject: [PATCH 430/581] Improve animation of new song select footer buttons --- .../Screens/Select/FooterV2/FooterButtonV2.cs | 66 +++++++++---------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs index 2c841f6ae6..21337327fa 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs @@ -24,8 +24,6 @@ namespace osu.Game.Screens.Select.FooterV2 { public partial class FooterButtonV2 : OsuClickableContainer, IKeyBindingHandler { - private const int transition_length = 500; - // This should be 12 by design, but an extra allowance is added due to the corner radius specification. private const float shear_width = 13.5f; @@ -68,6 +66,7 @@ namespace osu.Game.Screens.Select.FooterV2 protected Container TextContainer; private readonly Box bar; private readonly Box backgroundBox; + private readonly Box flashLayer; public FooterButtonV2() { @@ -137,8 +136,15 @@ namespace osu.Game.Screens.Select.FooterV2 { RelativeSizeAxes = Axes.Both, } - } - } + }, + flashLayer = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White.Opacity(0.9f), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + }, }; } @@ -154,7 +160,15 @@ namespace osu.Game.Screens.Select.FooterV2 public GlobalAction? Hotkey; - private bool handlingMouse; + protected override bool OnClick(ClickEvent e) + { + if (Enabled.Value) + Flash(); + + return base.OnClick(e); + } + + protected virtual void Flash() => flashLayer.FadeOutFromOne(800, Easing.OutQuint); protected override bool OnHover(HoverEvent e) { @@ -162,20 +176,6 @@ namespace osu.Game.Screens.Select.FooterV2 return true; } - protected override bool OnMouseDown(MouseDownEvent e) - { - handlingMouse = true; - updateDisplay(); - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - handlingMouse = false; - updateDisplay(); - base.OnMouseUp(e); - } - protected override void OnHoverLost(HoverLostEvent e) => updateDisplay(); public virtual bool OnPressed(KeyBindingPressEvent e) @@ -190,27 +190,23 @@ namespace osu.Game.Screens.Select.FooterV2 private void updateDisplay() { - Color4 backgroundColour = colourProvider.Background3; + Color4 backgroundColour = OverlayState.Value == Visibility.Visible ? buttonAccentColour : colourProvider.Background3; + Color4 textColour = OverlayState.Value == Visibility.Visible ? colourProvider.Background6 : colourProvider.Content1; + Color4 accentColour = OverlayState.Value == Visibility.Visible ? colourProvider.Background6 : buttonAccentColour; if (!Enabled.Value) - { - backgroundColour = colourProvider.Background3.Darken(0.4f); - } - else - { - if (OverlayState.Value == Visibility.Visible) - backgroundColour = buttonAccentColour.Darken(0.5f); + backgroundColour = backgroundColour.Darken(1f); + else if (IsHovered) + backgroundColour = backgroundColour.Lighten(0.2f); - if (IsHovered) - { - backgroundColour = backgroundColour.Lighten(0.3f); + backgroundBox.FadeColour(backgroundColour, 150, Easing.OutQuint); - if (handlingMouse) - backgroundColour = backgroundColour.Lighten(0.3f); - } - } + if (!Enabled.Value) + textColour = textColour.Opacity(0.6f); - backgroundBox.FadeColour(backgroundColour, transition_length, Easing.OutQuint); + text.FadeColour(textColour, 150, Easing.OutQuint); + icon.FadeColour(accentColour, 150, Easing.OutQuint); + bar.FadeColour(accentColour, 150, Easing.OutQuint); } } } From 09c52f03d8ea7ed896721a9ce629ec58d8d67e0c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 10 May 2024 08:25:16 +0300 Subject: [PATCH 431/581] Fix "options" button behaving weirdly when clicking on it while open --- .../Select/FooterV2/BeatmapOptionsPopover.cs | 6 +-- .../Select/FooterV2/FooterButtonOptionsV2.cs | 43 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs b/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs index f81036f745..648f536bb1 100644 --- a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs +++ b/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs @@ -188,9 +188,9 @@ namespace osu.Game.Screens.Select.FooterV2 protected override void UpdateState(ValueChangedEvent state) { base.UpdateState(state); - - if (state.NewValue == Visibility.Hidden) - footerButton.IsActive.Value = false; + // intentionally scheduling to let the button have a chance whether the popover will hide from clicking the button or clicking outside + // see the "hidingFromClick" field in FooterButtonOptionsV2. + Schedule(() => footerButton.OverlayState.Value = state.NewValue); } } } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs index a1559d32dc..2ed8480b46 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs @@ -2,12 +2,12 @@ // 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.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Input.Bindings; @@ -15,7 +15,10 @@ namespace osu.Game.Screens.Select.FooterV2 { public partial class FooterButtonOptionsV2 : FooterButtonV2, IHasPopover { - public readonly BindableBool IsActive = new BindableBool(); + /// + /// True if the next click is for hiding the popover. + /// + private bool hidingFromClick; [BackgroundDependencyLoader] private void load(OsuColour colour) @@ -25,31 +28,29 @@ namespace osu.Game.Screens.Select.FooterV2 AccentColour = colour.Purple1; Hotkey = GlobalAction.ToggleBeatmapOptions; - Action = () => IsActive.Toggle(); + Action = () => + { + if (OverlayState.Value == Visibility.Hidden && !hidingFromClick) + this.ShowPopover(); + + hidingFromClick = false; + }; } - protected override void LoadComplete() + protected override bool OnMouseDown(MouseDownEvent e) { - base.LoadComplete(); + if (OverlayState.Value == Visibility.Visible) + hidingFromClick = true; - IsActive.BindValueChanged(active => - { - OverlayState.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; - }); + return base.OnMouseDown(e); + } - OverlayState.BindValueChanged(state => - { - switch (state.NewValue) - { - case Visibility.Hidden: - this.HidePopover(); - break; + protected override void Flash() + { + if (hidingFromClick) + return; - case Visibility.Visible: - this.ShowPopover(); - break; - } - }); + base.Flash(); } public Popover GetPopover() => new BeatmapOptionsPopover(this); From bc6f3b8cb41570e8a8d85491abcd5e75b096c064 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 13:48:08 +0800 Subject: [PATCH 432/581] Update iOS logo --- ...0-5cbe0121-ed68-414f-9ddc-dd993ac97e62.png | Bin 453879 -> 394881 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.iOS/Assets.xcassets/AppIcon.appiconset/300076680-5cbe0121-ed68-414f-9ddc-dd993ac97e62.png b/osu.iOS/Assets.xcassets/AppIcon.appiconset/300076680-5cbe0121-ed68-414f-9ddc-dd993ac97e62.png index 9287a71040091fb7a249018bff4133b91b12e1fc..7b62835cdcfa85d60814f17a89653e2b389a520f 100644 GIT binary patch literal 394881 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 z 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

DatA2W^x6OxQqa_*YszP3 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

dN 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

~Hp0Ng>X@!#&} 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 p)(cq3~%~<$) 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
7teB@p7cU2&ME)tf)cjk^ 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 z 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∓U&Os)M%-|e`T>a3l!b}};EaI2Y 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⪻Vj#%ZZO(pEsHP1rtSEfo>#2PAE%NO?i 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 literal 453879 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 K)~(TTivAB8ZcmZ` From c25ed2cddde9764306fbbdcd490101eb6fb72aad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 14:12:32 +0800 Subject: [PATCH 433/581] Update desktop logo --- osu.Desktop/lazer.ico | Bin 76552 -> 76679 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.Desktop/lazer.ico b/osu.Desktop/lazer.ico index a3280f0de0b6a935309c84594e8b83cdb7d2e43f..24c1c29ba269aaeb38a9b9c604a1c364c986a753 100644 GIT binary patch literal 76679 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 literal 76552 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

X~%=`gEKj(Ksd$CM;}+tqLyrwl(h2dj8hG;X_w+ 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 kH>Hhmey{g?x&fFIPJqrc*zp~?i`MerS--#2`8( Date: Fri, 10 May 2024 10:04:55 +0200 Subject: [PATCH 434/581] Update android icon assets --- osu.Android/Resources/drawable/monochrome.xml | 30 ++++-------------- .../Resources/mipmap-hdpi/ic_launcher.png | Bin 6403 -> 6088 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 7232 -> 17696 bytes .../Resources/mipmap-mdpi/ic_launcher.png | Bin 3582 -> 3280 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 4040 -> 14331 bytes .../Resources/mipmap-xhdpi/ic_launcher.png | Bin 9537 -> 8745 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 11248 -> 22749 bytes .../Resources/mipmap-xxhdpi/ic_launcher.png | Bin 16409 -> 15300 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 21398 -> 35969 bytes .../Resources/mipmap-xxxhdpi/ic_launcher.png | Bin 25006 -> 22562 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 35444 -> 50294 bytes 11 files changed, 7 insertions(+), 23 deletions(-) diff --git a/osu.Android/Resources/drawable/monochrome.xml b/osu.Android/Resources/drawable/monochrome.xml index e12af03bfb..600c070c3e 100644 --- a/osu.Android/Resources/drawable/monochrome.xml +++ b/osu.Android/Resources/drawable/monochrome.xml @@ -1,24 +1,8 @@ - - - - - - - + + + + + + diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher.png index 7870430484b7a5d9433afea0af2fe479ac0e967c..a1f717cf6c414a2587644f19444e404289b33a16 100644 GIT binary patch literal 6088 zcmV;(7dPmMP)K?bN@1CEcus%YDx2@Z)UA^{YG5V?qu5RwoQZgQXJbnpE=?zzcLa_lb8+e5hZ;dR`gRUYqeLV~Z647kK3 zL0O8AT7jm-T(=% zF;?O3llJq`Xp{j_lOxb_1xWQnq`)D{l@keMr_-?(U)VFqQGJjH)bHVX#;YsTt z!0~#$IfF#)r%#^_Q>IMe->RxgC@wCB>gsB~-r3p7zekU@^L~4KJG8d$OB*hMqQ*i* zN}P;B_8<{i3&xA>#+;e7Rx~nJ$Ygre!0dy{&Qe)WUtbS%=FEYG3l~CBQ4!=q=g*&q zUAvm0xw#pCcWDk(0MQs|EIC>`Qrxq~vTmaSB>fx$C#cTZ#f!-2^JVqc?zrO)1mz9} zXgEc?&cWcZ(~t~Daf{$1F*GUkf)h7SMJf1eD@U{;u3EqTMcBT5dm0DPIC2eZ;_+B0 z91hQFZEelyCyfEng0(Qr2_()Ta1;lP$2~J=&V*R<({{SRZ8&f+4&*bFH4*(!&x)A|@Lhcw1a6uNC9|i3x3Wa{ML=!X@Edsh?YCJ6 z7|!m zLcmT#{*+lCjRMeuW01j1&W5V0s$lKfHCo|DP=roy+yTjbC!x^og(9~PJPtQM*eL;4 zQ9D9}d}HZfz9ubO5`jvXNe1d%Bn-w?RdB^)3pGa(H$C;#Q>@VQ3=l$6AEk-f2<0LD zokfrbO_@>$t5>g1S)(`iK*ztn3;xasl=%V>@Dw08E*?G`bp(dNA+ex+0!NC7GWY}* zT_XW>JDkXQZtx?>)|oJzdhL-KC!y!S7f^W3BvkG`xb@b#2vUazNd?R)Uu!Hf>d`-X0YQpn1gNsIdIbWs zh&m-}m8~OKRaFhoKfjv6Atik5xz_>qeg&l-KZ=!4Fud$WC&45enM%Bjbn)U}ONm*k zy}oV`N4Z#+CA!Z;XUicdzG;dEY2Utm3=~;8vTVw@jAL;rgi7P0wAs&@bL+8WG7+SG z;vi)i#v)?v_3MAF#p=k(6*C5rZ&a&vk)FZq+22`) z79&U<5X$y+=V(OtELrjw8k|!oR-U7MP>kR>B{)hnOgSOJF;<4$uTiO5oI$RdLv-NNtpE+rNuZ&_vY#_rHUj++Y) zKKLLXoqlT%4E^t?Q0y)M!xWPv%L5V)6D<&^456C5W$CG^)T-iX)3V*8M9oTp%-Ksr z%pfWKGQSI*#Unj%;x{|^c-E|0aNm9R>7<>~MOo0IG=NAsQ}uylRe7T<<6}61CW7$9 z6OZvedBc6L??tilti%sfY8-P}d&&W4298yQAzHr}# zhJeN}^go}%z^5m;-SXwjp|G%!Uwrmqh|wU+Rhoku=syC6bPjbi7C1B*il>xlb-nUF z1maHghFwCxC{B?Zu{YTCh87o_5;l-qlgvfx3}|4}#7p*R$?K-_lSm1ZJAjU7v8NDv zUJ-#hiQ3}D4~CBBHr~L;*#M-?rioBVD`% z_gFf7Ax;q>*f}bLxB;Z~39eA%lF4RR|^7XlM=YW+9KEj#> zX=F2VX+=H-aS+jYRD=a+Ig;^|3Dv695|&Gef)XK+lU0tPvUDsWme457MW&e&KPz2X zb5_AjYH>?=9Z>rG{ZR7ky%6_OKjP(3SkwQZ@Tke6`ThQkb0Qr{^P*0=HVtZOYWTRX=@6jT1WuRBX0lU?*+HH-6%mt4zoe8jO_bg?7a66_B9K^Tsfwxu z8o1IL*Lo%)^HKa9yg%kc*AY3$SzZK*0`#nt34RGK?0azUF+KjdbLWnjlX5R;9Llv( z`7RI~WYj~JL=Efe>QaDVJ||^yD za;Ka_4Y%mi>2^vTWHL~(L|lg|@zjJ;z870G^xC__tO|%IFE!i&Rw0$*9s)>MT>%iK z-*T#NU3ukn{vABl1x6U-9G_rY%cgv+%S&M5k~!eHx<;RiAMb{<>(GJgIWGoAt2S!O zq2w>;f$NHisrwN0?tgg~T>Y5)x&2Bg{qsM8zL%OI`iC~wO%5kcy4(-d|9n5hu+Q%2 z-a?>KE(x1@yeENTPn=EhTIBc{PK(skRO?`QI4KTFC!^kx3qaHfCE0l(gHah}8!6Et z{ea+9{o(-)&QO;~uec|dL;16dAXyV&PK?H5Q1ayM49>t89ngC~h=LoBOCFgI{e$Ph zarp%CTs)_A8gZ6mdL@gL7c-4mr?rs6h5 z#6dyGlI+p_Q0G~2yWAp`V+ylQlm6Dq@ALJ6_6}(Mht1%KBw06H^Y;%ypspH9kh3~h zy$=4FSAeTL0C9}|KVGq((-(9+Y4NSdMZFNEk`SbPqX-VX0G(K}be5F@ghb6@t78hq zF90G@F>qk#` zTDh}(VCt~MPD#@oaZdSMoEkfG!hmYs56*(Aw9KBfvRDF`Gp9}sVn zl%nTc42mc=L%)TRW$`{zmVKxTO2JtXU>AWhf|vd2O;FZwExM}La@)Z}C!zP(jmV*W zdg3%mvWRwLph_Y-)m2f7l0=-@P76uMI+RT@7=X@3{xz@T2SX z+Z3jq#6?&!x7}(XIEjw4aO^)DIawF&=mWRMhvyL>920SOGH*5zp+-m@keQ>mL0cpe z$$+-OIbUvwAh4-UNbbRb#Jl@(kWtR+70;WYLyjbh#)LcTIgv$^IQPkM@JuX*N%!0W zh3Asc{U3jXj~{;lcKu|H?%P#e0?DBm9~abC;YovE8gWwKy2*T=>S->I8;a19_HA#4 z#Qv|K&?{}DhO7h;*5eD5um|P$Uxwn z*wzfSKfVoW{^olyaP1_B96E)TsSHZ*67JTC-@niKBH!&-L*>1*VZvQAq4(7fU;-+A zvQGMCg@>TKr41_YzLATE6_4Hq@y|NJK!so0Fhc^=#ljP~c`8i(sR(~gJ-ZEt4xHeu zg!153@$uxzlaM3zAwWheN@h?2LuQYaVw&kx0HLxy@;UC^DatMAwYrgi9h{rN01>w*xNcNK4n&g}jSx?legihTvp_1-}!yZt(FR}@3lUw<#{IbXf;0VL7# zL{4Jz>y7te(!IAp$$SK5{tSIjN%D1|$*K%b8Wa6BGoDk#S2o}NMU`RAWQ zZe$TaW{7$sj$!{qE`1}7o++bv-m68C5>`bha&7;(1x4&CD7$GIcmjS1 zcl5yNrh|}Z>%{U;5ywbF$Irr^zkLA{?Nedm^|dUPk*>3F_CSaO;39_yO8iC8v3V!- zf6>W(xv?)2(Y}Kr=-YV+CL}!^yq?BNE%{#-zSN%GZBXX$Gcc?J&`T$7Cu(au47pWn zpp`@{M0+X^?UHeo0?AG!Vpzk5fq?-wnT04q;iKJLvr>t5pVI^7J|~=SZin_A2U)3- z2>P*TSnLge7r~+X0;otm193R}Umru?JFOy_kD`ZDhC&3#ht*d)SL($KVM{w4-}xE( zStRmq2DlXaqhA`SXUK#T|Mo76xgVcz_(8{f1y_GdkN?o2LwV_-o$6@(!oLXS-h1yo zxclzAp#t?o-)nogn5PxGCF)j``YggpMk7&LNXc=LkucG*i*i4VCx!#WQM!NuB4~`F z-;#8=I8Q{~7GNdTO(BNEE9&ncLjzG(1Rt&J06&VB!PVMCEP*e@#j#!hOF={=UQ~U} zWz8gl*{ZBq)+<2i4Q${34)5~{3=AohZ=&5e8F~;X9`v>ec(>cb7RO1}1qbhtu00t6 zkK4o7y>hik0j4F$_n~{;UeVs?@$zaC<^YcBEn9Fb@=8uFP8u?r7r`xT=Z%mylXnul%G5kV~*- z%N9NkV9D1vqef>6Rklz+hFk`8*y0#7nKDhvAUGX5Rbt9C28Ap9i(*Ob!r?H)&}gVF z9u-L~-j}~>KOqfFEGo{MWd-P=U&CXdGG8oJQ4buJVCO`b@_xx1rXZ4(vh7|XPEUA+DDp<4h?^EodCsXt)y?4g3jHj$ zZf+;qT=wI6dfYF*xM5WBQfr<_H_@h+8G_W!%yU$%4+d)+V4TQaEqS93hK2^Ar>7Sd zENB4tghI>*dtq?@aTW<4jA5k8GpvLi5W{Gnj@5OcoaJf`36ff&&x1peh@wYt7sWL1 z-VmNVhZ}N1XdL$o=S_#2#d9S%FG5R8%curOm7|G!s6810o!=&7c+aZ z2@8$Px*vV?5g(V|H49u&`rg>e>%t1v_@ z0wCo#Z{9rii%QVx>)nN!zoB?~8~Mwk1wxcWxo=n@#0g3ye-Me=$*VomKux7?u7=whlcI)&cncXvI&cM%p z_IIq0oahZte)>L$6$#NGA4qHw7(5>q-+=LI3@cS!1S_rfu6|7!NAU}ByJTAnQVSr! zc8qx+^q3hO8CI-Wvj)1myT|qI)c0&^aVmyQo9x%q>|QW9dH;A^q*k@6>>|Zsy}f4< zsBV})e;$Sd1u)^3DnQ2XfAo zXCBhv&YjQ4+@*yYoXwjz zj~YvkP1ahJq&L!MH;gi^kIt%VSqrPJt%cRAf2C_@L!mBc`=^%y&JO7ZX4F5!MrBxv zOO#PS4;jVGO`!&IEv0rZ`^f?*ojr}OlZcbWdiB-UvRNdRi9$_HjkUv1j&LoBi!krJ zAKmw5xpnM*tR3CnQqheyn7l)K3? ztw|)3D{@)duiSq1fys}4^e`-5yjXJ+Q9tIQdm9hJ;HO_f?94d{ju?r`3XnIg)-`fk z2h+P0Qc!4H0LH-nWA?cKWM0g zml9Ex#-(!v*7WJq;D#G+;7TnynZ#Z8x)piB=g#$`r@jXcA3h8R4jklwlK@e!#(0X@ z&N-%BD3NaZtv?&@6V0qtz5En&4P;67xN6uHv20F)r51VH+o*8HzVMr?8Mf6Ixl-ow zSSt{mb>p?o+7>o6-1c+qbQN4=vIAqEhSixMCExk~^Jan7%@y*uZp%TJ{?!Z+}xR0c5lb$?eBZG8 zv{MC(`ShcdzN+Vq(MJfx_h2E&k#hQOUOx?kxw5jd&4@1+6kOkd97$DimCrEEE)Lv@J@|sXrfRr;zxe7BFZcV=!fnfG22SXtTs=l#ClH}me?d*__roOABEcP6>!nqSH< z<(KlmSmw-`b1h)38!}|bjo#jmbQw5sV1LF)U*ADNjOmQoK0X8g=I!k}2f#Vky{TUd zLR<7fUyNZK)82iq>f-C``zt=QQojce9^8iO0vVsM;#ObZfdu&Y_+Ank`VF))_F_zo zZMbzS{oYq+88c?gb;c*@<1@J5fB`;X-rhb1MpC@J2iEar>q z(pJA_B&>ifD-8Ei_k|3QMg6`iit)+uxi2elpkF^vCc}qVLGtwMFG}h}*jR@>czF#V z508F?ad5Ho=L!*|pokI| zV^+J^w6y=%s!nMphi3Th;pzT-Nl+x&MrVm13{hyvdwKAKx9Ds z{{07h1{sYqU-6=D#8&f1CMHEEr^Xy#RQzFc=iiq`j)Ec>Fkfp5nk+sw=;!6-mBKRg zG|GO(iVA!@b36(&R^*rkyow;QX|no0X4K&)F9ypQtz60&Ag@*!Wn@cw#;D6rR@8vr z-h&=7#2W-+-TW`lh0yPEW;lfgH*UH=NK$h5@9+JDQK$d7D3jDaqPn^|s;#M| z+RAFGDX*mJ(h90BDx>P+GOFS8nu;o_t*WNF+FEH_R#rwaF)_4m-8vdMa+KWnQV7x^ zI&g0Ff7fVh#>*o}nIA6;^Xe#aBe?Sz3qENUZ=K>W(#E>QY zYsy&o38lThmC~LKqqKlf4bN?$^x4}fbLnSf{pV4-a4M0iE|hBh83hFev}x038Zu;P zi(JekQw<)do15DkMqOK6)MAf2cIE-gF{nAu@$u2JW6ibflQVSaFbW9?A*HO0op(wsSer-vVY#OD5vps(kUBbB?myWelrxkW`aX6yOL)_Pio;loGJ zkt0W_va(Wh4^}EOct2%M-5{it1#A@(Dg!@kG0>;LZzH9@vW2p?d_`4-B{DW*)`krm z>9NNivyr0-sEm6TtJG;6%&53)#c8Y11T-Lg0$*0?JzX1?2>66jy--7y*LJ=fKgyijUVUr^0%H(*M4b@}_K{QYNP!0fuvXz;)F|o!jf9*MWHJlrYK)JuKFWd+*z~ zPht|s7?E4;DwyT<(RKl32JGFtw>NZe($y3}US8gzAO#y;YPM=_!HC)9%P+sIw;Ygz zcu~SnS%anw&WrtwzqPu??aaT;?X=&;ew+2Nwor~AleFfbkOWIYOd2?FkdUJ?ZnMmQ zvLX);k1f!>SvOn(6Vx!H4aj$La_XSdP`{^cQ`mX}&_R*I%r=?l z0D*x)WU*MqdaNvd(hh3)|=7PxVUfnTOU&;C!&yP`SRs9GMmxhX8k^A4jjKmViUWG z8)l=iZDMF2{{8`yA!DhK8M2@9CU2m8h9ar{e6rzOCjwBv&F#(CjK7WJ*sIH)yq

e&FE%k)kw1nZ_Ituv@| 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 Rc+vm>002ovPDHLkV1m2~j8Omp diff --git a/osu.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png index b2ec3e49da191d5840d72a7ae2e5421dd468fecf..36651206e8c7c4aed92db0369b84c279ab962465 100644 GIT binary patch literal 17696 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 KQZ*oxkpBZPcw#^R literal 7232 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 D#&zr+ diff --git a/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png index e22f2565625261d8ad104f2d09c70ffccb5d2ee8..85f308442952d8b6e5dab778b955d5055e9abeda 100644 GIT binary patch literal 14331 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 oqR+3uGXD=dF@LF?zS(*JxNC=6p119V}|-T(jq literal 4040 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 diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher.png index b5e1a9e3798869f72105dec0643dfe18e8634a54..28be07512ad61c4d8caae199e300b5746ac047b0 100644 GIT binary patch literal 8745 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 Tsg}M000000NkvXXu0mjff3M09 literal 9537 zcmV-HCBE8;P)Mo0li0kjF=&n>D)X+PG-dAN2 zRs>gt&=Mdag_N7#2&n`@`~L5pIdks0=a$?X&_#dedEV!oGiT16nfIIdzHiF8w@#g( z!_VR8@N@V%aHwm5)9}$pAKlWiW5?T`dFGirLPA37`TP6d3E&)YxQ8_6=M167cl^H! zp`oF_VDfIQVesEUtr=EK1A&w7Y;ubPu7!?dM!eR;W7^Y`AuP2Vn zyhFn9F5|lm4dJI9P*EfC0RaIGxrO#&tYdQWosrGwSNM2Y0oOoqaQCZ5sCaW)7edL$ zJMnIm!BzQwGQnK2z`($!-MV%As9U#yq^@24DJUpdC*4ReUtcp4SQ7#Q0txwXzTy}d z7=-w1CIQ(AgVj|M_l}Qx^ytylRe68RLFid^@x&8PH1qcln9GEhfb`&C3vi9ey2N_g zYr>5H`5@lkzZ-Sw(p4M*yu*}M2!xN9aE>@l7$scRxhSJ5uQU7fqX|ahxy9edt!x5U zwiG0x!c5o|KEKSZ^cqM)yqQ#QpvYH(0C0{tb6Nu+09{pSf+$5*a(qC5<6D>=^V%Jh}t{KbghA~ei5OW$YAj&LNTDMI0mY#K1 z*P9|3btHG)4~(te;?%GTBM-9V(kIzH)aNX}Qa`1x(Lo2wK z3w6lQ>Yb>ZpA3}S3KbX4UQ#0?7e81~FGS1d^Mi_f7*k~cE!XMQ7yo4Ma^mt02)^G(iJ}{0IYT+>nyTC$#IudH=d-slvbM372S1Izj@Yg^T z{O^t(J5i@jopqPcNx?nFlTY@bXP$YMUU}tJiins&pMLr&MMpeQ(;a^xTB`R88{`PlN6gFcKCP*wo*Mu7SKHQ*Z8 zUvpK1qw3fOY2V*CK}ndq8|LWMpyirsL8?Lkgu(u^sIZg5yG75Qz3A_M{|9~c*=LlM zl}(o}U81X3uX>H@>S{W3<_vA!x|QC2_iyynQ@^5n#Y)P_$)T#MDhEQZUb#Y- zDyry0ehHn=ETqb`e5y>z6UXyeMRcL4lrEjW;PBiPe(wDF^OTsFM04jxQs2Iz+yi89 z1<)I05Aa$5f&*{?73u5i+tbL^4GYS^U`Wm{%`|vCT0ty!)4+;+Po@EoSMT00(b~0Z zxwU5rvUK7uUlD|Y;DUc0qm0PiWc_$6S>IVt)-h`+ee_DQj$({nMbgKw4&1&1W?IT9cZbZ=6 zQTb)-9m!XaT^nl0Q}RK;^y$-u9yhjFkXCjogL2pGr_`~lDShw)${Vtj&JA5j<-^xd z<%lS%8o8FS&V~_dMSR(?)l@um1?3E0LTN)5P}=(&C~wPmRFRxxFI+y4k3UUg$G%k? z)#CtV54dM-sUz*cjahqElH|b$mJ8Jj-T3-7R|feE8Z?;p?%gX?gWfvJtElu?Dy4HP z&vmR@Z8$mu?g1BAD0r@v1(G*(8KwVe1!d0NP30`u%T*Wk zcb1lxir&|!PhW>}dI=b1(eV8I{KAdg)jBAH(xXR@dpPqmn(nt-=Bo`xjrxo5Sd6W{ zA~laPB0eSS@Fm=WSBlmQ5)~A&id<*$#{0%|^pFY`MqxM$;^n23xp0rL7e=)N?%_fl@_09!h09(Adaft%CM2s~*cgZUp%-wByHrNM&VZdgZH2DkyK$x0DvXkj}E| zs2qvbZX+0~MZp+fTR^Zy+_O^uw2TUN9hEjhD+}D(H{KZIP;PGkj-b+56@3EkYApcu zAl7d!7Rxzgem!$MAb;Y-iFEetSy^FtChW;%OxZ!%!@LvcHy%vCafZZw|e(6~Zs@TWBYV++q7Xn%XbiPsT zoQqDT^w(DjZ>54s0I@no$YTY2JAk^DaWdX6y?VwS%xMkR$PbMm?Jd5x7I4p$WlTy+ zqVVvc!n0HB0jQ5{TEwxg3Wi=#+@?*N=HSw+v4PV4tj9ipU+?ele-(2(7_gu<)KmZ? z1`Zr#Gq|jFD!Ao?{Gy@DHIfzCieQQIVdgk_UCv&Ov-Gw!&h9zJAs_V)xVD8Xg!B(K zQI*yc;HK{1zh6v+$fe0ZAqX{+HrLY;n7+2~D7(ufaOtrvQ1d;lTD5Ax}w&Q18?Y)yh! zLAq=Kp1l|@=bAN9I{B46$Vz2DA4V7CORQocfNV(sAO&L|=L#wxb9_yJ;NgPm`8bI~ zIboEma-`P=RWxD3M6YZgU@*qH&6_v((dcz61TlZX>ZdOr)G~52?cf}k)y&S$7KO+3 zLPo@AR4`(&lv>L+*}9<1%aYx(jO`ovf1(NrVfHTHlhx7tqF25AKp;s<=|H1*loCtA3Rc90bp5B zwSElqGbU~qh8IK?aW6pkMg?)ioDV8qBp~l0&i?2aS2TE;fH>#p&O#7+S~Kv>N}0c& z&Ybs3k%ugUyvDs;^n&I4xEDx+jMcWMpMJ)n&hDT%WZMzL+}77Pb}59qojP^;9Y0WF zeApYHm8iv(^0*Ajc$F*0l_})PVGafrL+jGParpz6h@*-#r!ihDVIe;}gFMB#IbY*5 z9;5*Z2g^Khj(1}6F7?mLsk%gZa`1@M2-#C8JJ79LU@5!y?ZL4Ir=hODe_(H=|GgCe z)?Tr0i^4*8=0$xibSVfZ7`#**%>Xg^gOuRaVhY|*)Q{t$~5T%OC(XlJf+RHeNP-_;7sz9m|MV$;uzRL?_n_#H)L#w5xRDJ|l17 zVuyIdfkcs)CJe{>@;rCIBFZzpBWKWJD*ZY^Hk$JnDd+7Clxuhop0&QUR-dB4*aq8S z%p6#>6+ty`qz0^V+z=ur=XgN7mN|AiCv-a}+~chPG)arCAgZet{p%QI4O+yNStKgq z45+B+A$$&qYZjO}ot_r0Sxc(`Lh}YmlJPE;29Wn;eh>umHs%AMj3oz7>jKDpd%eg{ zJqK8aE~4_7blI?YWdHWte|6wMk*!)jW)9RfrgD(R&#&wLIyOV>5TKjxv{)>$NbHRO zKt2X)+qP|&0$>+3d)^n6HDIBrh$Pt_IpN$AKin?5e^qP-T};lW^GDLCbmJi^{Oe}Q z9}0qH+B_yMhsiB`XET*G7OUL7b-WpGTx^Hn1;mO2*6^I-R|JQL!yw_I+F46 zHp=S1KvY6NBLc_V5$p{`eM6UvEA$>?M0NH#Dqi#z<%Y{=Ajo_s?qXV@-3|vSY#yny z?k83Pxv0Hl!>0vNZB za?w+e9zx*qW({0One)HU-??SW7Exz24@v+^7_wLR>r0i~BQhE^XmGzX@)7FZbI(2Z zaYp&b%v%o#JJ8g$^M!&EN*xy^^eZX@5=5my29s2>?Fe1DC~d{%i`7(`l0$_@lc?O9 zulw8}uwdR^$_SfJ*1-#@?3+X>+$z@7`(h~LvqMxAlcJ|Tw?Cdz`bSdU%KhRRlW93~ zb_?M_04PKHfcaDrD^O%->jtvg3E%=*$E_8DL|O4HdO}A3`IP?AW?cZ78?rbN z0082pMv!x75Q5DW4JfQ%zyAM#Ycu%>w>2 z2qdM=l}j%gg9O zmNbIr;;eL}e+0#ZM$oY#vnYA~=T!LZX-a-?9VLg(ro2@)0c6eIMd@Ml_}z;{8PW#K zqjF6E*xZ%QWJ4&<1OTANWbpGs0B^6=XGl++I3b8Nb6|u0@}+Ss(5O+PN5Qq3e1uz? zG-+}_KTxh7a?IE@u+0Q}G=%`*0r4;%704D{f3avyXx~Hn1n;Q)iddR=TgRi1)^-K zVPIpIdjZ(PHYpcSaqqqN{u*4H$w&CbgAYFVfTflK7&2stzQGE!pXsAkXabP1a!bNA zZ;4h}5SjS)YRa0hoi10%?SvOH&vMUMMJdyE=zDhxw;ZCxfsvH8{s3L6HtZ2)J%6dN zj7q;aL3tB)Qdan4(VDZC+XP^p@hN4p{#IpxiYikAU^R)wp(O4#N&uHCr2ya=Aw7Dm zX#M>nDTRBq8VjJOUxxsEz_potgkQiwSZb+)u=J?+fb;^fZ4ea!&WcIFEG3Na zrL*N!v}`|}37bpVh8~c~DhNhamE#QekTT7kL=S+Sf&jADeJ!>?R;1?AnNiDx(10{1 z9!Hcv`Lz|?17rh8N=mMk0HA{E)vJdd;9LL=8#ZigF&SaG7^A*wbiLU&G&EFnO%$+- z^>zA~HKOpM`-aXH=P9f*3U?nDY2a~KCvB(GqnA<%>va&Czb}^Jhq2CH`lYUKGnao! zDFY%|RYX!OtEcE8Gbr|>C`wtskIp69wlr23l~eq{Ih46%uM`01mofKWl)~=@kw8M| zEV1QLlrg86l3!mz$$e+j(E$;3V#0cgd3`aR4xLG<{pM0ipV^e!caC@t*Ga6dl+a^h zVrwaYfWkZPyc0ISxd86I`|d`ZaXKD?>jd(ZHP_aLU9JdWuKd&~sk^by#`^9CZhZ^6 z@R5|xdKTv?d`yklsjHH*W2uz*%3@(u4-K73$HO8-3%rmm7edhD;>Rqflwk|0c>Q;j zAGwE8!WU9}zgcv+*JS#(?_|pVk8Out;;5yRGHI)R4da?RXo2u_tlSV_NH64-h+qcgt2d<#pQ06d$y&(Kj zi__IH7>0X;f@}jZZAXNF`iMdKx8|2%73|w@zm>$XF11eBCXC`4Zbj+PqYTlC2hXO` zBspCT7qa|#novV&?B?Yz-A9!P@*dPu!PjwgqW?_FobkEn5y-b>?@7vdXFa97vV;;| zTTW$(+T7EHixfA4jq)oCsj5Jdj9yl}{V?TD{3qo_?4pX38M@n=zxx;^4B*}|gk4-7 z8(d9EZ9Q{y=SlhW(zd{pki@-ScSZ^k}5K#SK|uj;;nS- zPxD3kgm>1^#Zud#4L#^`d6jKjA%w-AQ~ZG06x(MyrLOq5?y)!n1a$JXCBn9(j$WZp zHv@SazojHDvmjpsjQ82yG4kE~u89*TiTXNn;2cT-ojd#OXO6K$!leKjFwrZN02JZs z(|`5(zq1~I0!^AUNv}xJmnSH(?<~=J;D6(YuAIu}C;QEyW3Md~1FFirVllWv3$KPD z%q}A9j&JDbE0IFgq_X}$6*iM%|F((>q7$hy|C}IR^av&y*E#<_PRC!FN2mMFq(s*9 z$NSHqVo$QBqP$qes8yr2eh}F2Pxc4jncy$M}6Br*0@DQol~^C`N|RE>O@{tUdJz7h0Yzo~TawfPh?VI7^AwwYokuBSt@!oS zIgAO8^_@o1BWKg$_f}Bs)QuE7Wg{IKx0DVInlAD}888M&44X~IM$V-ZlQz)dHy6{< z-c!XryK=uB#kS*|s8?!gn)o2lvHmWBb>VB!8g1LQ$A(=@#yPZp1Vq;J_4OT~2|!qH zHOuE(_1N(2v(Hg-aQhjLkM9WC38!Q<5qjL=M*~9a}u3k^>LbsM7$zM zK?tpP1fA-`9t#r~!)b8@@rXYSb;sU{N)MqW^kbFu(sVl7Yl=+EtscTL=8ba*0YZs+ z@e?}z68D~dbL{SC&HqC8%J%L1in@1y!lCZ2U|f)C-n{wC;JA*99)NIrvu4dYGU>(Y zVRMG=fP!t>v`H?=LoaVVNN0Eug-W6;vL2)g9uw73NxfY|aoDMo-wEdSVU1�>IX}``GPV z1gUv10O^im4~_B|vLbi0N|__niaR8*!9L9%k2xMV&4hpybG+R>CSM4_Ng4oTn&T88 zz(f{;F%D`ik=@jS?a`t)qb{F+zRUE@u_usfob^9e!|*FyZnr05fY6|Q`}RYX9k6(@ zE{qpJ*Ug$WOJABSkIST_k&A@xH3F41$B9a_PB*3jF>$@8JET=O*>f5TZ>AXKLICkl zJ$w!zFEi9Vbsy zWEm9Xlr7e*5kCSg*dP-v`ur=%I%&T6@6P zx8r1)c}<(N z2nYar+#K%~VY4V}@m{eqrVQ_-Nt0_NUjcQNyW)b-`o%YOzxmB?=YUI%Q_q5?hVEu# z>=zsyEPm6uCSwfAOCiV~zK~NE7V*m`-+gCmQP|*g`iN1kS2G{Ucim;DmZB0-na1n z`|l41mmaAfL#WFS!ex4>Q>RV~k$Fu5Fc!jijjoBWWzy5-N|b0>Y5A1+zflz1n@R6I z%?=O%$cHdaa0@=gy#edK3Ls0rHr*4~eQjxCdQBDineW>@1BA__G$#L2x$S2Y@PVHE z9>BBKZ>qESsHJu5)~mpw#-+=_m(!q0?7jycc%U0+R^Ul5a0LhfpQ@@|6PT_>Pe`4( zl}>WyF%P3~^o2=+cw?Lb$TP>e1^0p@&&xR`SI?gsFkAe(im~NmVX0TI7rE@UAzw~C za4#rg*Rwk~^zre*{Eyc`Y}Df8Bfh@A5prNrI{^UDjjzA{2Ic1F>a7fJUgm-?gb>68 z8~2DK5P&w(27=VNx)yOJ*rn6RR`-r^FF4wBvVB?sPB&;a<+87@bUpy`vA&J^n(N>h zx{KD|vSrKp;7;SvyVkFQW(yjz6>rU+PNMdb8pj2$03l%eNg#{W}f-`CMu7&ttq4BL+zmD*Oq+cEwUGQrhfhU7IDxsZQ67c+)cdTdV9iv0RzOkDn5RbD(Q+)N$E@W(6M2&=@1Jd`nd^oh=HRK z&f?5K8kG*mgHQj1qMw~WC&JjPdTlA?Z9Axc3oL9BdpkRJ>=ZiROg^sRP`F!;mFe__ zcY5rx$KC>GjT$w&8yuQA^*X?J#WGs6wr$&OML}vB7r2A+mU{H)K}(k|<;vTZ1F>NX zyXrEQ{+mvYoJYrB`ox530_Y_+e4@d4jdUuGpXmAg7dE1=H%P+q| zUw!qJtv5g|u{yxd*c9!L6PvW+xYfq=pJ6YIKGP)BxiO9jj~&E=s$kfy@1(G{C=Dy)R9!QeCgNA{r*UUa|}9-=@b{tjkRIJhW_BHR-TWWa0@o>^6^)X zKKke|Zo%3QmwKy;Y@hk7kt2EozBm~_e!TcOO7oBPVFV5JR2q{;c{`&ieg3~FY5aPM zXL3)CUP7lvEv7g=o_=d3C4an$tc&-E-xe9L?9y-(kK^Nk?NC9S`vZrsoJ-+=H7We*tb+3E$$hygqE|Y(l>+S~*G%m!?{Ma$|JqEl;8Rf?qzU|w0*jqcp{NsQ!xaeV;+UbP4 z8;tG`M!pJo;q7d2+`|R*;bRLfWFYs39X21|)|xzrTe<<3%HS#0=&~AHsG5y*)nYw`oJw%`1BYvVety`&rW;z0PkQXJ$Ht)!sEbyoyR)Bp-CQGKhR1fUIwyoM40h-cMu zm5*1jg+#7ni(Zl6qD9O3C^O2AI%vk$?XKpVEhv@LkSl@#8+t<<9x48Ty@(ZD$vicm zqHKhwo=m;Mrh3#-otugE6#P8CdBE2{mpgZso5-Ms*kTMhyJN?W@gP$hN!zI?1s0TOok%A?81(ADUc`UA zC}?&_yifZ%X4dg-;+r*VHiygfG|GlDqO6*igI0gjtDKqw3Xf6ZUFgbOIgA+~h&C8W zHf`GUjW%uCY-chupc>?eTJIUUP+0X~_%HqyTR{|&vw4Hd z&;?~dnNT)O-9Xp>pd;HKB0ztlD=R_x)RC&G6_e18TU>V*##nBVQLLMfvEN>IEwsq^a z8`uqw@bPK(I`TrE8tJX^F1!=()_i(QA^+rwHwR@SuxkLzcOU}O+&+xQ>&W?akkyun zYmZ4ksEb=~y|o*Y7sLo*bk~6MAin31G+J6r7eJ>Y59EbBkvHCfcj29Q_m5u3UvEI= z(ej{kK?u|U20Ji<_-!5hEtC%fE-h3RdI|^yu<2C+*SLo?NQ-CiEb>5J$P;Hu00000NkvXXu0mjf$Tga! diff --git a/osu.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png index 1cc3fa9072d0bddb5511ec97d47bd80a7513c78a..613b817f0a54e6a0d14e606d07fe4fa8611554a4 100644 GIT binary patch literal 22749 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�d`^ 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 literal 11248 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%0D@fov_`Uo9QUkSQ$r4(*a-{?f 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 F{})kc|3?4- literal 21398 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 JIpTig{{TBU2Ppsm diff --git a/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher.png b/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher.png index 65751e15c9432447c18a4af6a03ec970ad82c945..2e722ee884f65d1932e8cf3683d2b10c28251bda 100644 GIT binary patch literal 22562 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 JPDHLkV1h{Eakc;e literal 25006 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 E0Sm8>N&o-= diff --git a/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png b/osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png index 05c6829a47625e8c647bcd3141594d1d5cea7b3a..f22db0c6168001e8f817147c13be1f55b9689cb0 100644 GIT binary patch literal 50294 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 literal 35444 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 Wm9}oz-ruXr00f?{elF{r5}E)w6xqZ8 From ca608a8443cfaa7ac6b8f1204235e750fd9fe89c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 18:36:44 +0800 Subject: [PATCH 435/581] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e784e4e0b2..5452a648d1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From 4add1bf8163c4463c296b04cfcddf76b77acbd5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 18:38:11 +0800 Subject: [PATCH 436/581] Update metadata assets --- assets/lazer-nuget.png | Bin 11930 -> 17094 bytes assets/lazer.png | Bin 328536 -> 448852 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/lazer-nuget.png b/assets/lazer-nuget.png index fed2f45149d74aee14421e74fa75dc117469d01a..fabfcc223e742536a3939e4ebbf0187484146450 100644 GIT binary patch literal 17094 zcmV)=K!m@EP)ez^2$vzBsNi`Rt$LLTrkOOB^S5h+8l& zfK741Kd@;brh^R#ut2DRkWjDN_wBxN=R5ytckX@nt#*YFA|C^*_uegY=gv8E&i|B| zA$p^{QQjzTlsC#7<&E-2d87P)S3LKJ;NPD7c>NY3gnTE@^W0pLluQGXMw<;(&1G7zq3GpZ^@cy%%46aayrh z9L42N(&=;?9~%_}0q&U^t7nt%`=VB>byX^rO?3~5zQlxifX{V^Ls;-kmK0a+-o1M=2#4?t;N~1|qkSX=Qi0!LHr|C_%@>CuBODf1fY3;u zKWW*rWvLr)yzzKO-T%r!?o@gOWpGy)^}V!{D!aS*dp8CBWh(PAykwbw8&af-IZ8F< z2v{yRzK#51mOopmFl_>*oAOkgKAv7lVMe%N!-frCnm2FW238FL%*?|DFsA7>EF3q@_hym9{HY6DW<$s z%>2CwhU5QmJm~Dvi*jGtn{rufsB$M6RU(#C=Ve;WbeK zM&z?@O#Skgzg#wY^yq8ij+sUUBYp3E&r$aiFH&jsR?2|rsf;B&MtPdQ`*O7VQUZ8V z0vK7NH-mz7(?jeLiU1W1U@GjKuZHKL?r+-RH#y`(qK@f?9?$d?wyNRG91aw zFg*U{KG?K%>(=uwzWCy&xE}&Im*_QMfNk%{KJ?NPB`HUnM~&6!@0Nn zxtD1l6Tauk4df40DCcJ=p9bMGOmH6UUP=-j4B*L;uI}^Xf9hM~{%vs1Q}=@Cfs_%uv=0&|fXnAb)RKJe?+=ck-|RB&5N!&pvzB zRaag00=t_7I)swokT8H2`QD;Mi&76g^w1@RLg7-{kFEz=-}s+*(BPWwR7f!@XTs+h=Yp>JiV%I0Ui9PRW6{1u|Ak{>br39E@pjMU=p|3JX zZ7dNc4?`mGv%6RtTy@GRr+n+&bI+|EDwS|3Fo5)iG000l{q)n{*q_LUj^B36Bed_1 zr>QBOW8&wus8@sskwJW0xmMlo!^}59)<>@+dGTx2HBO{25;=@t7d;jc5b8UBsnpn# z0AOT%P-1CNstrnJXg~WnnsCnI{bB@HC77VotEm=VZ3ciF*ITk=$=K6RKm7+BjXQU! zi+fnyzx8up<63J_GE~&W0Ghtu{(;e^&HDVljPXFsNhNn$SgA)J} zfe|1PAPs6`3N&%~5*oW~;gHW|E8#bH-F4UBzUy7@+AWm$JX8#DP*nix_Sq9QbK10N zKWCHcPa0iV<59nz^?b*j&rpF!y&CyhIpWjWlmj8qFkg4N5ypW_bW-A2g22doQQdF1 zkx7Ez#R5a!n^crE;C5=O0j49c0VG0ir9>^K&Z8s$=JX+b35YjMS{`=^K~Xzj9&^-#N$G&K;3bHIklsnMz)?NA zQkOrB>Ja)rU;_mZ2dJ+)NE6#eD&x(E{@H34-viRAjCuz5Pv=AI`?{qn-YttyFVc1Ll~^VZ!kc;EC~uH&!K4_ zKbsmAV1oPZyYFphoN>lM1%?L;19;$0c4A~W;PG?z>7SEy5s1I`i#JkgOE7hS-Z!l8NZvtJ`d! z=V>*eVhwOF_c9YeB?L#b(wr;b+YlVWI+#0m?(z?O-~(ku73m<9m z-Y*VC{PkbFnNnMOB=Ir&;l`CC-#$J1{J^LIw=)g!Q(vQuL80nU{TxaQx8{IEc)fFP z^fP>RoV@j&K#{H!&fP(*24U0)m$Zyyq`5EhQXTj_FxFjmlkBUcg1&wJ`RBiJ!wolN zka3jcvlwZ4MHs*|fE$(S?(V*%sj2C6jjmny#hZw>bWsbxY!f?oQX1u>NO4{Is#dEOIN0O5?T*?{j<9}o~u@Ck>8Mg zaB}7SEH(4M@VSrBSB;o}y{I4M=;X(Lu=PctsKbY1wR|WI3vLCwFL~LA&kdhZ*p#lf*9=ov-y^VC`fFUE?O< zMNzx{gumSn!+c?9d%B~&aD*CCfzW}C`Rn>US5;`#?zGuHS0tzjFTCGXIR;A#iZh8T znSe=BA=vOd+jig}s08#8Ok(ue%RjuAk_!6zq?1m%^ZM(r9}9oWE0F>z_s8X2_Z7pq z$s}^H9?ERrzWs~2TrN@X@BGaQ+I!Putn-WVr822>ojce3ihJFJb6oz0=;u*FL)gG> zv@c#cT(iFC4}Com4P|3{b=OAQSyzwqPEcK7UQZ<^P{1*R__LG=tnwLfXV1E=BnoM2 zo^wQfTbK!E&z@c25b}R_baVvw-+%uB0fG@r0K@T^jKRU3#f|hna_)Biw^1ae2r_EtKkXY~- z_HgWAU{r?Ye*Jxk<{>lI%fJ5huTNx8;jUY5xn;z8h9fcqby_pcnl%e1-#;Z09|c}M zf8T%%?5O-aO?*b=5P?Ai!c)X+7bj!{khy1#_!jl|=xXi#E{<@YWp%uZ_C5DrvLGM= z4h2QOyAmh@C@FkbufB0mXC&YmRt3Yx0C;gd88dzCvBzGTbj{$d zPTKwR$7F*GI(sK&V&s7$Bwj5gIln6JaUCUr7S)CvRc4g1h1#sMIyd|y-SP6bF#%nU zps-URfs9EQ(ZRx8VU8&$4mr;Y`@)qR(Gl)i2e4Y8x=ah~GaM5U=g~L96*pQE$j;^$ z&ZRTbI@t1^yA%Bb0ECMdFTQlkmMtlF`ulLoNI;+mu<`K256{f!^A{vtu=6)hY0;lm z<_;-fgHb0tLU){01G0kxo2hGxE6#zGQIBxXibmpISe+m1%7{qzxgE+GcTr;^+tJ@4D+5Df%-qq#G(}5e{AZVrk9!#+hO|h4d%zLYg_> z+!5&{qS$X?y>20jr+1dvA=$XulNwk=90uZ+@rv zx=N*Dol5SLq*VaZkV#c!4{GO~f0XkflFH1PGrzWC#ftPuq`!lT(|B-H$$oL<}52V?)eb4Kkw;N``dZ z^q+T7Qpsd8C*OVd-7|HZU|38rEC$f4Z=_E=@x(;n2RpU@!L^iU29U`d!lUo z=u#sVp%%dTW{4SpC7H(eqhL;u<1P-L9T|a6$JtKwqIH;pX9HlG;+cBgy1lSXVY|rz zq>4Gi=c;4U)J>-yj1pdN!J=g*Ed|mF99xy(GJ)c3={^Sol-KN_?&mg9QdzQO36gGy zEd}<=0N|wk>i%0Er94nSMn81Fel&>dOWiNXp-Ds<_}Ub*;~{WIR8-Up;m}8;W*g|y zhXY511`a_bD62Vv7$<>~%(X;ODJ2|+^Bkf$?`{FCK39{#)ejlRy`Ly*L5Q%8(kk$E z@XB20h1Yx9iopaJB+l%-7-Ry&7ie`QDpFk)om#*Jy#FQ+A4gZ%xz z6&s}JPdmAJ=m_XGExgH)Q8>0f{x$m8TnDDX=n2JfC{0~0>yD5>gxK9YUpZ9B9)~%m zI#*a}l;Y=9`FT+#D*gB-7&a74Zt#P%*Pd~ZW|+XgE(;&rJ{yq}t@SPRvXBQXpu z!35~*fi>F_r2s4f4$pra76E?lPZjJ}0wCJg*VmUGKYsjYlFn~u2Sq`Tej_3SsmlU2 z`Xgi^XZVD~R3NCVL?A{_e2hi(xaSZ`#43%DD7;Q5ie|D|$pleV;LG-gJ`~+9&#Ad? zB!vWZ+gd4CLN8~GsX~b+C^4R)2@U4AQXp)B{6rhg1XxUx*?0dcN-B)g>yz%py>< zKi|-PJ%K$vJ>DaaJaSr+*AHm4 zv@S|G=Z=4J&Xott3`A2lch8LuDzmWa9Md(3C<)zX5k=n#|H9*^QqmpZ=iK0$f^n-u zsw*>8Opb&QQIl5xBmMB6FV#TblP@Lu1+v-f{D&TT=rni-_ooJi@&roLZ|BaP>B*BP zUzl`r&#KLm6U`KCazs}+poAkP!`H4GQ^I8SIxp9o^ zLCv^W0ZdTQlEJ!gP5yw(5l*|vQRw&z#}>?1Ltxud;j}$YcoFRrPlv|Q-b%TwEDc9u zn)i~EVevN@$O*Ga<%lDWIEN*-uK{;MF)*YGU|r&|F3B+XJ2~n6p5LyZtfwY#pk-*h z)#08;5iRF1hk@Kij0QSVC!I#MY6N-5ZSs0_Oj?XkL{+%Y1JO@rADB!if;m0I>h98Q5E2NF)1`TrBf`=0QxP{D0Tc4)sB<_$5FJP zUrtH^07A@`PAUL{P~Dh}J5+$EAp@Xc5Fvl~!ymBb-jVV10Dr;e4y*eO05UOqp~aOg zF&H466A-~{!%{}-znXo%+ZKDZS3h#xK#}KwOfaMLKzdPKZN$E%2~h|LUCEpEeGidZT&SMI)xmPXAhInQGJItAu zSwtxscz$c5V+df7Rrk`}yLbB|kO1}tWMHYQt1C5o_Uy!DkIpAvP#!-&Wz)Hp_sh}Y zUbJe%#jb7x>Xjo!Lw#Rzel4nZoqA${L`N!kT6B|n2bDzP1h`dQsxnBaa=BvbcbV9& zJ4n!>?vbigG3mPFp))HwrN4so%?AWoVKn)D#6KIG5km<|fKh20TQT%s5{(=a%Cin1AU(Sp)EN^C3L7^(q@z%#HCDvsC<)lWz? zHH=Lwq}i8_BvwfgUM>&n=S5hVLMrMBq(M~@JYG^qw#xxzRaKOQ zXa_ZbrE{r4X{Z9Q)^1x{TblL%TjJ-C@PP#z(q;-5H&TXs9TUy~szAg#Qh!6K=+R?d z$sETq|)lQksPv=$%~L&A)FS~5+sbd2J;DFx|%jd@Q~0s1B?So&u^nzf1)+8 z;4`23%otV!UL$KDkpUnwX3d(Fdho#qPf0ot^q-dCPp!p+j!-&KaV9yVPIU~~-v-G_ zvx#3YYB!UX0*a9oIMkgl$T7{rTZCJ$*8ed3Qo21c@J(UP3WRpAeyh1M{Gc6f=^_JuniA!cS8nsmz}w+620O!z>tJ@q#lcZ4vZ5Tc0by z;I{S5GtWF0IDq1#$Xt_j5G!K$_xGnJOqh^3k-c}-7QI*l$x z-&hOg-Ho0!`F~)HisXDNc%wF=5yOTntoymDMx{plChOKPl2Jc0Z@^Q}vCbh)iEJ+C zJe$J0oOURuPsaW>3ri zb7V|PU;{{!s$NG(&jxT@y&-Oi7p20~dtq?N0a2~mfh2ie&Zkt7!of{@l1K#{un@Ea zO_R>YCKjkJfX?7ikz&*A$fTPpFYlo|a~(vSLH$MqhI&5!LIyxF@`cK1D^mWfg;Y3o z4yDJoB;Hfq(?bK#ZKb~3o~GKKel;hc&I}~mVS|H%(0fXXFoB!SnomWpKh>cA8rMI# zdOP*q`ZV=#*&_)CXr%yQh6YLlls|F|jsCmS$Y-D?`peY$Uk^}e?N(}PYO;bsvbtV# z&a(*}o}3Ea^6tg54Wd+`zPp~Ifrnqvi&c6y$dU9uI2Jhg1J+Sxpe#YMbt?|CfUS{Em(eFu;QN4G8@PeJmccMTwY;g`$MnvM6l(;Au4WuTR}CapI-*FBeh!=if<{oFAFoX=dBPbqceu@U~-U^oN#? zxc;_xpFquj@h0h)kj^PK&!{yYAwCM$6wWW6rU05}0Iiz|PG)gG_ zFYUI8IzYQ&MvW@2*+&j?m*Vik6RCCCF?66MKW{R%Uvj2gm{DQ}xjb7SQNLW$m9a$2 z+ZP^i{bS#=gv88txwuo6QFkquOrBaG6t*DJx_?07APs=Qpuitq0uy4UPfg>3c-By~ zcv>ArFM-Dt@9Vn4tJ3n|LAkdlv8G9rCXK?NcDL0rrEYSe*+7YPT;g26^6oBaCS~(Y z@`c^S8KUAW1a9bz9DBA}-`;SYC@Qh|D(&i2jp9+(I%87eb@^kCpr&^nPu(oC&B|d= zD;_4C&Kg@cVo~-z`x0%rXN8)7*PN&JKbb+}PdqxYevI5|sb9XAHoxmiKq}k~3c%_x zodd$nn(^LcH2I9<>+Wek_ju}mbUmdzO00^ilq(jvQw6oZwGOh-w*PsC*8j(CYK{V_ zWnXu55bFg7b<-|bN|WCj){j+52iET-pF1AdhH}Yj#7z6bCvZ@{P>{!sBn;6>J#pjW%NmFc^%SOOycP#*OoH zxm;^fWqn)sP?0;vI!^R);ruIKk>8BDj?(!o6;GbS1g)=bI}>Le69*yX_FS`?ID(PK z;<3_MrWddN4GpkVm~p|f$bHc7tt{$0uYVN7jh#~LbO{C%MJ`qn#~MV4p98x7|Qu;n0kN`rM5KbK$zs(D;&1|~X*G8h$J zpXKzV=HnQM57T9!GDzdjKDO=}5EcY(V`~BvA>q-S{EHjSU7fz>93#Ucy zZ#``uZT+v`QERcN7ITK(oxlK7&OMQ~uH8e~uCkU~%wU2=W;#nHZd>oeYiZ4ctL3xN z4y;NF$I5{2+(rcq9_TrL?q$Bth;EdL1G5=A9C`ksTPOM2AohHh$zTo+luDFm=|>tM ziUyv25T6>9IuOY5W9S#3Ey-^|r~5;n57h!h11Zoc`U`i3*Hiwlm>I75l#{*629uES!@(=LBEjrqG} zlw(wmHY19OJO(UP)0QsKXqG;svQ2tUgYs~~q=RXfTs-N^fH@Wq^UC+9#^#+&>(V|H z*zm68tm|tnA&e7IIG`GmOeT(0D*D<$4yvTW&Y&qU?eD+;{^3XfC2kvW4FW;Z3`uDt z%nKJ~G6fw6NdeDS)9lGf)llDx7pdrDQAoXJfbK{WT29Nwom4cho1HaCeTvVH%}pLn z14!ecbJg?6>bpBL%1)o%SL)!y%(>%2Jh}k70h6`brJev0K;~ zRCg>fA%wkvL$H#Fev7y=g%piwlnn(ZbVQ9ki7C5>Pvb|)RB1iZYW?-%*Z@ngYYO@D z%q(la@(XEM47Y$m$9{MjP5QSBNU$G4Z6V`DrLbR_Al(r z28>ec8w{-pt724thJ7rX%BnUcS1C^wvx#--0S(Kp?qP|jVf5(Hf}??n;ib&DwrEt% z=?M7F3jig01gKQ3`T|kz>E4L0+Yv>@W^TRQ5F!EK^=;d0?Qt*@a72nt!{@(n1Fib^ zTSv?Y6E6KT3Z$c^CIdT}Tylr{da;X;c(HX1$z7BxTEgfbx0g*#cvSZ!BzErj%@a}r z#)g{&egy`;Zhk^eV`<_kkmK3wwaC-6dptkn zDfB@tA#E93vZ-)rAou)n`cc2C2*GinIxT6P{c=yg4CUt1S-sL$T7pK5z@CTJ(8~Lt zm1?4O!Bpw^fdeQr#0uSTA{E~@pZ4DTduc5+>IAjYXE^Q^P}63evE3UX2rpcn43Z5J zaLY{(QK_ewM!$Kk#5y}}e}aNNz2v9!D&tCbkV#iFi)6a>!$UYQSfb=ds-#c={5TUd zas*-1!$hMpJGPAm*X~vqIpHt^N@NE)E$dA{<|!sZ)4U@{tlKU1sPDSF^qU#*gU*(O zN!WU&vb94xg1o#^Ln%E65zEg4%~u)K53)a@`+?PRqbcBp*7--#5oa7nquI19P8eNR z=jeAFPdjdWL=tFlU$SS-AJrsnaL-ryjGnvDF#pE#@TD?G5))PehaUyf#ZM_!gWy(Q zUq5aA)nl~r7Z1z&a@=+-9tp3csOWqPBAs}nqLgt}4A*i#;eAlpB=2FfF&KsfAUJ{K zR_Xk(#JBgGvQoSW3W2Vf=WkZ-Hbt&=v%5!{C;8Y29=H50wCzi`GV<0#UAv7{A9@y8 z;cxu%6O)=krH#9#3lw6psKp|1fnb*?oJ$y)d7qs`>6*;FL;Syg1eBHQsZ6_mx3aFD=J`{oog;8ny+lex_Y37~ z=U3)X?Q9x#T73X%*YEx)L!cR%`lX_82)9oD<^?q6TNhC)hhgOm8YYtRClslfeG5!l zYVq0L?--yLesEXPE0R~plCi)06RZks50@U-Y|StI(iR)xZ_LsKiFG5H#y&?ckPV_X z9eo5%JZCYrzj+Q7c(6#~>--0Nlvqkg;#+KK-0s?wz-`Gk*n5ESTqo2(ocJhp3;yu!|jva|!g)EOSj3l$13q)aBM#4h9WPIXsw-5fqz5q#6n8v=Fql16BSc=S=uCt~) z5W$q@vli0CkDQs*3}%35rOt;Pkzy&(`W}wGpQ-{p64_sT3bk?FChc6VQdX(a*wLdY zUGt5*6$lI(d*wtWNo~f6;n>>~xXuDbc8=r7vkNT? zuH4LyphQ#}u~m$v$zO9UQb+al^sGTV*}Vo6Le_os2Xe_*I8OR|`>2TQ7>nB6`q(mH z-*)Dya>L^v^&T7k16FY^*|Jh=RN8AD*#rjX*Am`zuV>^NY`qLj> z(r^Xuz9%=xc1v@N+nb#_mWszDi+@PM9pAfGdK@u0VC57}nZrzeM%}e=SZ(^mb>t7Q zMZ$J|vAJk-F39a`d^sio+h?O|9J9+&0@f}I4j3d zKfRwa47|LW3c8ePW!nAdYC1B30Z{Mw55JX$TRSlX<%Wc1>Y42T{cs<%Y z+;(qfv=3ab=20rJSX?|k@p>>*ZqZcgynCg@H*&J5?o#VM7Gz!W9{T{2h|d!Gd488F z`yB+5ec~bEI!6$5nP85QIhB2VeXEli*EIVG8Gna23ORTHHSnF|4_E+RvaCFol{RV% zzd#}IY1Nl+Y3Q#WPC?GVQ=j`WWxC7M%rbL*tS z46v0|V9?gq79=^Rk}`tiH{8(K*_k-!2%@&cHfO%6s9z>LXLOTkr<}dwrQ|kn*beTA zPkf)Yu?Qb&K~l%lpZ_uWTnE-7GP{PVIrAJzWY_W^KYtxAKF?hBGwONb1+^uP-U1K04yvoG!BQ_MJD*s`5;h;1 zW@d`*XZ@F(mdpTq_Uw6#O~9JY2dr~VB&h(Hfx-6e+iROQZ+>pZjC%1_m^(?&=hM+D zCpu_cp)RV2mktDLV&KKGP+E8OZIYmq-g+F3`IDi==f0I2xZ|Ip-S@4O(K{?fgN}3; ze$MDnZeQmBt-tzqzW!-C>Rn4{;*$NRpH+HFwDGn_X*)Y#@~p`U+(rbV1{esk>r|$) zVRbHIyMFzD+%CO~GcP<{BK0BbWx{X$?-jJ^rUxm*CZ*gIOSaonvx!OhKepfgIE`7h zkea7Yh}{3e_5VY)eSMT^2C@$wP0^wqM9fG{$4p6V$EHo2R`Eb~cPWpQBoHVE?t}>w z3g@1C?or?W{`Y?$2ZGh!0b2Vv-&N}YHr%66;{g33bVJK>pPWIthaO`7K8pkQ~$bc(rn7{h%clwauF(sP~;A{jW>vTFi>@4!sP%zCt$1s(Kz8v zvn7Z#Ko0kHvq2H z9ZxMxxNK`^evp^k_kpHoFs3{gQkc52QXHKAliZlESzc64=6jx7{KC2K;j3#dCTrPt?2{y~L--l*0#%BIs& z{P%TtQ%h^B4YnwirkJ-JsJ6?U!?FDv+vhpq5Cf}zr8=Tp6ihVBrF z-UkKugWO`aEKNu$S>Y_Alq5c+P%0r{W`H0}^3>e%G~@E~>aJlc?(x>v)^{;2JDHY1 z>UDsl;f@o0L%x^$*a=@UFwV*jK7hd$CpKC4e!}xpIv_ z;pr%&FFRHeg}s|W8Ofbl>C}>Dt#N8ubIX(7yEHRWu9lWgD(L2Bb}Au@I1e|t(DiB$ zUfS8rBwQCdCBO9}HzRG-cGJ;I#l57}QP&g-$Y|6B+ zbN|wEv7@X6S)+>0lx0i7W4m{BbF0J)c^;8!z2Q_CY!P7?T0b!imudkd{qt-hwl+1( z5s1I$Oiu1s)RI+MNLb4lnMd#x=5i`JX|iiPljfz}tl>k2QVn|RdfeYuXr`PnfBtV(T+8i{c0dBoOH@(`@Km0%#e2d1DmIy6G};K zOWStBtVAL{`skzg@JE$t*>Ib=h7thr0QLx083#PVYM?ug1K`@=)HG^erC2-n zn>eOTxZtm){*sKU^>+8vJtKU#n1thhQrahxN_jFr-`r*0v@xY+lW3g7a8KHlZ=UDkvcpzV>ml?Ori^zeKT9x@dF<`7$7kaB%d4oX=S$1fB{oj26ye+^{a^! zCw?G)b2E#ceQS2eM%Rn{kSv}91kH2oBGC?P$jMo`742CxcZc^GKC^^NXgNZr{j8T; zT3c)<*9*SbeV)3n+F6xJmYGb(AzFBMlo`DU6PXfN2`^&zxz`EDrAt;aAk|yNRO}hq zo?d3;jG2k7?~(x|9{m&E`6fy#tp9({61>d%zlJSmsX-ZXzko%H7QuWdvoHDXq?-}& zsO3}IXB`HI+}t&z2260pcA;IyGaMzzl?&_+Un17w(KIU;Yq}#0e)A%OGSe2VB*;xR ziT}d?Jzd?@);>x`7Tx;1$Wy}hntDPR6`nUtXZ{?)ANH_iWOc60F%>MWXO`5Lm*|S} zY>l8@k^U2E!cEID$?*)f`+vdSzcTcHq|vc5)H;EJeE`xU_~(EA=MR@kr8V)J5jt&Q zHz*KxT@fyMqXCSFOPitu1jl0p8@vz~{q8CmHk(K@8fNLhYv!&~M3Lu{OLqG&znjfv zz^970>oBZ?U&1=V1JU}3+$dcTW7<%9Iq$4L@Xpyn82fPhDy5T1Yj&Rl)i-S%C2b`B z&pPX@hgqTm)C{@9=#UZsYGBr^Srv#9jwaocRKe(F3Jlr=NumE@24)1P8Gp9tT(AF9 zns^eBnLZLk@yufp4l4T_7$Ov*S~B&ty!9&wjKH>?mPzkM(Vc=9sW zz+st4y~1Gz066@a`S}))$hu6O&b@kc!CJt5O7DhdmQ1S41b^cijD7~}w_bVWl^Z}a(2n9njg=u)fGML# zkFK&L7`Wkv8?H?{4=GTXcZ5w6aZ5yZiXqz)X+c&_fE*k$T4t&nVDl}SD&=0}5Z!qQ zenRiUffGmuQp^y@zknS`${fUZxoDJgPBL7z7P;P>hiO*T3|XrN^17fFUMsB)^NH2S zbi1m~b>`Zz9{s7+QgO*FDmF;|xc1s>zt!L0UjohcH~Mu~MEWY8o@uV}*)LFFe?Z&L zojZTU9>PD3SA?m_Yd?0abdH#{16_?9)t#CuUT8a7v@F6H0%pEq9-Y*W`n;ZbXIJP# z$H<&O7TfXd+T*5{&DkUMsgQMac~?8YJ6yvHidW!1& z$n|914o3ISzvd%Um{^|xiqX%0|4%TjIvKN-D7r>vVrk?Dh?ije`0)UOgSXy#>zCg5 zzW3c8zaYo@sQn$sQO{i~AwwhP4tXNN3RW~5oe44OIV_^z9vxp{29ORC8%Gy}XhNA*xMT{)LO#>hP@Q44%oR)|M zCY^VZ%##arE}MvnUunLGsG2aGA5jnHJ|YRw`g{8$=gmEGN>s`Zm!{VzT!SI(1rg#Z z3ISWReIbH3#}N@^ks8*czr^3kfMid??@ax!Brw(#m=eMtnapF~ zSwFoC1*88fU-`<5Jo*QZKmPct5MoI4&y?Zr8ir(1X%e!{+|Qi$O%}a%>yf}OFy&)s zNswYpsIU&I`z^(B%*>LVU`HfLH?ybb!x#KFb|eTb6ZQuC{FuT9S-c(42DSOxaB8GNt;wK z`av5W`4yP&ji#o>OT2kpL_1P&rP2`{o_g3ZvD}H)N$uC zHdGwf^mTPpg6wfP1ON7Pc2UcyHd`P3_ORKFk30@AT}LfNo~=IJ6+WZ)QA}M43CJLA znh~vxwKeg#7u^flUM1AV5}h3!rq)@$jf#tzAo|)zp(_`U9L`moWvod2}ivGnq~0ZLIT8Wjb_pcX#*lvnuMb!()J9nSrJ~sPv+XE~;>x zdEkya?%00kop*jV=|VJg>OV5K7Bg~1EC@wcA=zml4Vvg4HfjOMrwEoHh5+*n#1Nvv zy<$I=I>iO6E*^;Af&f)_S9j#3Beco9mpyLlGpdR7DyhlOSHz|mc0steO0zCMKauz- zY?^ZT%2^_uHUYpoj-m+ zC2DwnR4mF45k_W`HIEz$gj=|hb@=pk_sac$ni1_2q0Y7oG-xZs9;fIijnp0EAd_%X z=dbY8{Q!eIIU~_<$CtnS<@Y671t>@U_36~Qbe;_sg_%5|Brw0#7sp|0%rt!poevDq z&44f}Dx-ha6jQCW*P5M7Bbi0^r6R3x>I+dh{$Q5MgppX=g!3NOoHT859wQ>p3)^Mh z7aR*pVw&5;^Xj&0kOh6s?%wzD$nRyxPl+XP4U<0T0Ln-}V5Gn*VC0$M_&}be!6^0< zeE67Sj!E2F7OP#Xzw9QezPwM(igDl|Ip8U(5$oYx=iYrZYD_x=vOc*aeAcex7mEHN zT1@qJNJRb;QDc$r-rLjF#VV*ZX5v||4b|Da6#rdcjLHJy&mJ{~=KkyZ8(zzO{nZmr zIN>|2g1Z(iT2y-S$tSBL(fjdMyUiR922j^>W3Ra43ZF#}>@ds_8MS%y<|`WE0p#DW z{L~Mr{PG@|<3|w{K>LO782v>(5k-O!Hve)7{{Kidv3~cQf6Z0#Ya>{}x!>F?&jEJyI8gAZ*%-CXcb4Yw$6oJBD_Pp~~$IT-8HS$~s8(5&~MXxb8BKXvA zat1uAXB3CK0+%sNefj`QKM=q5)OkY^AAO3xZE9-jX1(8!J|C{Tcc}7;834NHzWeU0 zu`VhzDCuM0z*Yu@A8TY8piKMN*))L>F5H_ginb=02vNM*@LBF7hnUh9)h%l=j!{cdax*7#K54+G%hq1q&r$T?RC+AcwAV^!A&MH>jR8F`#75V@v|Ee zAAR}NuYUET+_&4<66v2eZ{8sH`2p*Fx4dEoKtV$QL=Z7#UETZ5Z+>(2Raaeg@lZ@K z@w_E8@%^XSxPegzcI4>Kjy?OR3A1$cvB2qQ_d2&&iDG_7d<(H_MGX0ih=_>~eEVdW zYY_infN%?X_c>l&n`{FN(-qP*iThy6UngozS+9cNIbbUw zsV;OLECWWV9O*2yeEQR$KKk>Y|NM1H?qXBQySr)K7jGi5r&l>boO4)w0>2B*O}4T1 zxV`ajd)#%$#;&tLq1ZV$NEP+%N3X{+Z(xGfw${35C)I@`Fp05O;LLyg3pvNIQE`aB zhHd>`wzdY~>OB}E-;|NKg7;UX3zCCGzyl>_fS#{^{p)LBuQtL2C|Gg^xB!bkn?y|c zALjRQ=N6iQ{6jJ9D)=~XjErS#m>cy#5iuQ#c?0~Sdmae~P((w~B2tswoKmROL!&Km zp61*Sq(9~z$J5dL&O;F&eFoxx;~U>V-}O$PJ{<`j2bK6J2U7xs^{B09rMsB9w)vcM z&YAMVAO7%bj9`}zb>E)fJxe=&@fg*1bxV}p#K2E(L``7`SGAKulO^%9-ZZnr+q}5* zJASWompvbv`3|Bt_cOD0?(3lTv17?i3<*stmC%7bil%=2?J|XK$bMk7|G^J_@UwT{ zefL%jq)C$|4Z!ghU*6?llo$hy+zf0~3=@nUJ2ua_poPI)>&lfYFPS@c?uUoE52=OzYHltTdiSz@h zZ00fVcoR)x&t7_ng(^`tZQAtHY15`%1;QKRL#iI^k#EYY#sH@N`Sa&zcJADnV?9|! z9>FuuJoC4V6F$Oz{g$C_#9~x0UH3cMao1CFfe9}%{F9!f$s0RAWav4<5#jA#(tQcn z#_K|oXZyG@R(*i*Sh!*8$KFBN@vSsefqRz;{oUh^JMIVkvxf&xA4>wvP^r?vrAfTm zVL4bTAW|nwf@RB=rS8A~{#O4h>D_|Ne1yeN*vwEjwvs+_gxN1Up&Ykl(O-;*&44kV+@w&g>xGCzgXH!*52&{m)WR1~&RDIx^@Le8?(~J!JPg94 zK;-j)xSI9-j!Y)g%l%W@wryLP`>ghAiG8;m5(Y5O!nS2Pb^t|KWC_sxm9Ko|_>X?{ zqyIh}X@CNZ(EI#m+IjnvG_Yy6awu!JEyaPN`*3pbffpONBH zM={~smo3<@jyHuz`u1P_>Q`U5@WKlpVqM>t&*ytPIy#WlQ9)$yP!J#GRaXJ=!Y}CG ze*5jI6)RR`ncEAMN~MWAqj~k})qj8V(MNx1By~Y5bUePEI{B}6)n>gKM-{o zw)eTs)XmZXJ7{8YDFmO4sz?;`S}YjiMpV0}wwS{qxm)P8@$#=#6MncO-7Op_yZ!2` zum1MezV9e9C8M5pAD4&X}}$jWd_JIKrF&q_|&I9HG@aU zM>m)nn#DtI0e|L4j~?9yM&R#9F*uydF@?83@W2C?F;>_r4)ekm&e}C=)_nKOGtWE; zZ3p4;F65hT_H;+!^&Kr+?@3=E(^GI6o)1UtI|3E4n zPRw9NJKDx=oR7A0n_&ma5zn5z{L-aM4`0HYau@+i0<`@wfGy}ObQ*t!xWo*QcGjDd zpdR@9T>e|gfs*q%^7wr=pPm$luz=urCZ4T{zCe$5v~5yrYim2&3{ii$6aKXXfx{`X zecXvD7Oft8$I|Rh&T{8xm>Dt*9Kjsi|Jii8T+aOLXFoe(&YU?%A9d7Gi+BW1X6Z49 zUSWaR#=vxYXJ_X+e*RiESJzy3-F2&3C3P@xLsCZB_x&=K(ZQ-59Q>**TC}LjW^TYl z4-N}(H$*w)et-ihLIsu}6+HGP`q&1Qku|IbIF-^(O-&hg%w(8Q86JsgcCB(;CW}#* zVh}c&{p&|Sov@2_GV&BqCy&y}*d3LLC*QZbyIUr&AnAgctmpB^A78t9^XBgFeeZkN zZ$~bf$8D%_-BqldRUY|O_TJT)QSle(6m4;^k~Ut=a=1%?x*}9&t&G=T zJM;}3Hh>v?mIf&%LYfJiX2Pa=dwWw%s1y?vz!JNm;}iGE^Ts$W9zhi{IQQjzTls8JG{6BMS?0BSI|Kk7v002ovPDHLkV1i^7 BHID!Q literal 11930 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 b!4O3$rBAdmT`k@`=Kuv673nHTP~iUodp;H% diff --git a/assets/lazer.png b/assets/lazer.png index 2ee44225bf02b4bcd19b0f6590f0695027f3f1ee..f564b93d6f58e2e2243b8b07d1f578ff6c8ebbc1 100644 GIT binary patch literal 448852 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 xr?ZQv-Cu+`CC5YiIv=YP{w28A0vz5KIH5x|r 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ΝTvK+yE1&XHz`YHthniw>FjH07F^P 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Π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

-wwC`Zj%52 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%0AV1l?--1cn%xmxk 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

bTTg3sarrv0>eYawBo=o8tZJLG;Z5he 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 XR#}gv+Z#gw^pC#Ib?q`uOvL{IyBilS literal 328536 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 z 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 From b026309e36a2bfec75ecc893016de53472445af4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 20:32:21 +0800 Subject: [PATCH 437/581] Add setting to allow hiding all country flags There have been enough requests for this at this point to implement it. --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++- .../Localisation/OnlineSettingsStrings.cs | 5 +++ .../Online/AlertsAndPrivacySettings.cs | 5 +++ osu.Game/Users/Drawables/UpdateableFlag.cs | 31 +++++++++++++++---- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index f4a4c553d8..affcaffe02 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -200,6 +200,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.EditorLimitedDistanceSnap, false); SetDefault(OsuSetting.EditorShowSpeedChanges, false); + SetDefault(OsuSetting.HideCountryFlags, false); + SetDefault(OsuSetting.MultiplayerRoomFilter, RoomPermissionsFilter.All); SetDefault(OsuSetting.LastProcessedMetadataId, -1); @@ -435,6 +437,7 @@ namespace osu.Game.Configuration TouchDisableGameplayTaps, ModSelectTextSearchStartsActive, UserOnlineStatus, - MultiplayerRoomFilter + MultiplayerRoomFilter, + HideCountryFlags, } } diff --git a/osu.Game/Localisation/OnlineSettingsStrings.cs b/osu.Game/Localisation/OnlineSettingsStrings.cs index 0660bac172..8e8c81cf59 100644 --- a/osu.Game/Localisation/OnlineSettingsStrings.cs +++ b/osu.Game/Localisation/OnlineSettingsStrings.cs @@ -79,6 +79,11 @@ namespace osu.Game.Localisation ///

public static LocalisableString DiscordPresenceOff => new TranslatableString(getKey(@"discord_presence_off"), @"Off"); + /// + /// "Hide country flags" + /// + public static LocalisableString HideCountryFlags => new TranslatableString(getKey(@"hide_country_flags"), @"Hide country flags"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs index e7b6aa56a8..7bd0829add 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/AlertsAndPrivacySettings.cs @@ -28,6 +28,11 @@ namespace osu.Game.Overlays.Settings.Sections.Online LabelText = OnlineSettingsStrings.NotifyOnPrivateMessage, Current = config.GetBindable(OsuSetting.NotifyOnPrivateMessage) }, + new SettingsCheckbox + { + LabelText = OnlineSettingsStrings.HideCountryFlags, + Current = config.GetBindable(OsuSetting.HideCountryFlags) + }, }; } } diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index 8f8d7052e5..136478c7bb 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -3,9 +3,11 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; @@ -13,14 +15,20 @@ namespace osu.Game.Users.Drawables { public partial class UpdateableFlag : ModelBackedDrawable { + private CountryCode countryCode; + public CountryCode CountryCode { - get => Model; - set => Model = value; + get => countryCode; + set + { + countryCode = value; + updateModel(); + } } /// - /// Whether to show a place holder on unknown country. + /// Whether to show a placeholder on unknown country. /// public bool ShowPlaceholderOnUnknown = true; @@ -30,9 +38,21 @@ namespace osu.Game.Users.Drawables ///
public Action? Action; + private readonly Bindable hideFlags = new BindableBool(); + + [Resolved] + private RankingsOverlay? rankingsOverlay { get; set; } + public UpdateableFlag(CountryCode countryCode = CountryCode.Unknown) { CountryCode = countryCode; + hideFlags.BindValueChanged(_ => updateModel()); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.HideCountryFlags, hideFlags); } protected override Drawable? CreateDrawable(CountryCode countryCode) @@ -54,14 +74,13 @@ namespace osu.Game.Users.Drawables }; } - [Resolved] - private RankingsOverlay? rankingsOverlay { get; set; } - protected override bool OnClick(ClickEvent e) { Action?.Invoke(); rankingsOverlay?.ShowCountry(CountryCode); return true; } + + private void updateModel() => Model = hideFlags.Value ? CountryCode.Unknown : countryCode; } } From e887f93eca53131304d345517b798421fe363829 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 22:45:59 +0800 Subject: [PATCH 438/581] Always show placeholder on unknown / missing country --- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 1 - .../Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 1 - osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 1 - osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 1 - osu.Game/Users/Drawables/UpdateableFlag.cs | 8 -------- 5 files changed, 12 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 7a817c43eb..a6868efb5d 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -160,7 +160,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores new UpdateableFlag(score.User.CountryCode) { Size = new Vector2(19, 14), - ShowPlaceholderOnUnknown = false, }, username, #pragma warning disable 618 diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 9dc2ce204f..13ba9fb74b 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -118,7 +118,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Origin = Anchor.CentreLeft, Size = new Vector2(19, 14), Margin = new MarginPadding { Top = 3 }, // makes spacing look more even - ShowPlaceholderOnUnknown = false, }, } } diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index c9e5068b2a..165a576c03 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -165,7 +165,6 @@ namespace osu.Game.Overlays.Profile.Header userFlag = new UpdateableFlag { Size = new Vector2(28, 20), - ShowPlaceholderOnUnknown = false, }, userCountryContainer = new OsuHoverContainer { diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 27d894cdc2..b9f7e443ca 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -99,7 +99,6 @@ namespace osu.Game.Overlays.Rankings.Tables new UpdateableFlag(GetCountryCode(item)) { Size = new Vector2(28, 20), - ShowPlaceholderOnUnknown = false, }, CreateFlagContent(item) } diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index 136478c7bb..ac52599bc9 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -27,11 +27,6 @@ namespace osu.Game.Users.Drawables } } - /// - /// Whether to show a placeholder on unknown country. - /// - public bool ShowPlaceholderOnUnknown = true; - /// /// Perform an action in addition to showing the country ranking. /// This should be used to perform auxiliary tasks and not as a primary action for clicking a flag (to maintain a consistent UX). @@ -57,9 +52,6 @@ namespace osu.Game.Users.Drawables protected override Drawable? CreateDrawable(CountryCode countryCode) { - if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) - return null; - return new Container { RelativeSizeAxes = Axes.Both, From ea35ad46899b3a11ed026377a351f6f3d46593de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 May 2024 23:26:43 +0800 Subject: [PATCH 439/581] Fix nullability inspection --- osu.Game/Users/Drawables/UpdateableFlag.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index ac52599bc9..6a587212a3 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -50,7 +50,7 @@ namespace osu.Game.Users.Drawables config.BindWith(OsuSetting.HideCountryFlags, hideFlags); } - protected override Drawable? CreateDrawable(CountryCode countryCode) + protected override Drawable CreateDrawable(CountryCode countryCode) { return new Container { From f64cf5c037f839da3ee3d2a6e686bab1c1bcad0a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 May 2024 09:25:07 +0300 Subject: [PATCH 440/581] Fix button extending beyond unranked indicator width --- osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index 08b0407a79..2ceddedeb5 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -73,7 +73,8 @@ namespace osu.Game.Screens.Select.FooterV2 { UnrankedBadge = new ContainerWithTooltip { - Position = new Vector2(BUTTON_WIDTH + 5f, -5f), + Margin = new MarginPadding { Left = BUTTON_WIDTH + 5f }, + Y = -5f, Depth = float.MaxValue, Origin = Anchor.BottomLeft, Shear = barShear, @@ -233,14 +234,14 @@ namespace osu.Game.Screens.Select.FooterV2 if (Current.Value.Any(m => !m.Ranked)) { - UnrankedBadge.MoveToX(BUTTON_WIDTH + 5, duration, easing); + UnrankedBadge.MoveToX(0, duration, easing); UnrankedBadge.FadeIn(duration, easing); - this.ResizeWidthTo(BUTTON_WIDTH + UnrankedBadge.DrawWidth + 10, duration, easing); + this.ResizeWidthTo(BUTTON_WIDTH + 5 + UnrankedBadge.DrawWidth, duration, easing); } else { - UnrankedBadge.MoveToX(BUTTON_WIDTH + 5 - UnrankedBadge.DrawWidth, duration, easing); + UnrankedBadge.MoveToX(-UnrankedBadge.DrawWidth, duration, easing); UnrankedBadge.FadeOut(duration, easing); this.ResizeWidthTo(BUTTON_WIDTH, duration, easing); From da096376af7b84ec7c6590e976a1505ab33a394a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 May 2024 09:31:32 +0300 Subject: [PATCH 441/581] Fix DI error in mod tooltip --- .../Screens/Select/FooterV2/FooterButtonModsV2.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index 2ceddedeb5..02eb2028c5 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -267,13 +267,16 @@ namespace osu.Game.Screens.Select.FooterV2 { public readonly Bindable> Mods = new Bindable>(); + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + protected override void LoadComplete() { base.LoadComplete(); Mods.BindValueChanged(v => Text = FooterButtonModsV2Strings.Mods(v.NewValue.Count).ToUpper(), true); } - public ITooltip> GetCustomTooltip() => new ModTooltip(); + public ITooltip> GetCustomTooltip() => new ModTooltip(colourProvider); public IReadOnlyList? TooltipContent => Mods.Value; @@ -281,8 +284,16 @@ namespace osu.Game.Screens.Select.FooterV2 { private ModDisplay extendedModDisplay = null!; + [Cached] + private OverlayColourProvider colourProvider; + + public ModTooltip(OverlayColourProvider colourProvider) + { + this.colourProvider = colourProvider; + } + [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load() { AutoSizeAxes = Axes.Both; CornerRadius = CORNER_RADIUS; From ac7598cb6868764478daa50b195e4a39aef7da91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 May 2024 20:25:47 +0800 Subject: [PATCH 442/581] Move localisation to existing file to avoid silly new class --- .../Localisation/FooterButtonModsV2Strings.cs | 19 ------------------- .../Localisation/ModSelectOverlayStrings.cs | 8 +++++++- .../Select/FooterV2/FooterButtonModsV2.cs | 2 +- 3 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 osu.Game/Localisation/FooterButtonModsV2Strings.cs diff --git a/osu.Game/Localisation/FooterButtonModsV2Strings.cs b/osu.Game/Localisation/FooterButtonModsV2Strings.cs deleted file mode 100644 index 2cb297d8ef..0000000000 --- a/osu.Game/Localisation/FooterButtonModsV2Strings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class FooterButtonModsV2Strings - { - private const string prefix = @"osu.Game.Resources.Localisation.FooterButtonModsV2"; - - /// - /// "{0} mods" - /// - public static LocalisableString Mods(int count) => new TranslatableString(getKey(@"mods"), @"{0} mods", count); - - private static string getKey(string key) => $@"{prefix}:{key}"; - } -} diff --git a/osu.Game/Localisation/ModSelectOverlayStrings.cs b/osu.Game/Localisation/ModSelectOverlayStrings.cs index 7a9bb698d8..cf01081772 100644 --- a/osu.Game/Localisation/ModSelectOverlayStrings.cs +++ b/osu.Game/Localisation/ModSelectOverlayStrings.cs @@ -14,10 +14,16 @@ namespace osu.Game.Localisation /// public static LocalisableString ModSelectTitle => new TranslatableString(getKey(@"mod_select_title"), @"Mod Select"); + /// + /// "{0} mods" + /// + public static LocalisableString Mods(int count) => new TranslatableString(getKey(@"mods"), @"{0} mods", count); + /// /// "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun." /// - public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"), @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."); + public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"), + @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."); /// /// "Mod Customisation" diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index 02eb2028c5..d0351ac348 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -273,7 +273,7 @@ namespace osu.Game.Screens.Select.FooterV2 protected override void LoadComplete() { base.LoadComplete(); - Mods.BindValueChanged(v => Text = FooterButtonModsV2Strings.Mods(v.NewValue.Count).ToUpper(), true); + Mods.BindValueChanged(v => Text = ModSelectOverlayStrings.Mods(v.NewValue.Count).ToUpper(), true); } public ITooltip> GetCustomTooltip() => new ModTooltip(colourProvider); From a988bbd3cb6125c4f300f3d092d32b5e962365dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 11 May 2024 20:58:51 +0800 Subject: [PATCH 443/581] Tidy up `UnrankedBadge` implementation --- .../TestSceneFooterButtonModsV2.cs | 12 +- .../Select/FooterV2/FooterButtonModsV2.cs | 116 +++++++++--------- 2 files changed, 65 insertions(+), 63 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs index af2eea6062..4aca9dde3d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs @@ -7,7 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; @@ -97,15 +97,12 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestUnrankedBadge() { AddStep(@"Add unranked mod", () => changeMods(new[] { new OsuModDeflate() })); - AddUntilStep("Unranked badge shown", () => footerButtonMods.UnrankedBadge.Alpha == 1); + AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 1); AddStep(@"Clear selected mod", () => changeMods(Array.Empty())); - AddUntilStep("Unranked badge not shown", () => footerButtonMods.UnrankedBadge.Alpha == 0); + AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 0); } - private void changeMods(IReadOnlyList mods) - { - footerButtonMods.Current.Value = mods; - } + private void changeMods(IReadOnlyList mods) => footerButtonMods.Current.Value = mods; private bool assertModsMultiplier(IEnumerable mods) { @@ -117,7 +114,6 @@ namespace osu.Game.Tests.Visual.UserInterface private partial class TestFooterButtonModsV2 : FooterButtonModsV2 { - public new Container UnrankedBadge => base.UnrankedBadge; public new OsuSpriteText MultiplierText => base.MultiplierText; } } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs index d0351ac348..44db49b927 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs @@ -32,6 +32,11 @@ namespace osu.Game.Screens.Select.FooterV2 { // todo: see https://github.com/ppy/osu-framework/issues/3271 private const float torus_scale_factor = 1.2f; + private const float bar_shear_width = 7f; + private const float bar_height = 37f; + private const float mod_display_portion = 0.65f; + + private static readonly Vector2 bar_shear = new Vector2(bar_shear_width / bar_height, 0); private readonly BindableWithCurrent> current = new BindableWithCurrent>(Array.Empty()); @@ -43,7 +48,7 @@ namespace osu.Game.Screens.Select.FooterV2 private Container modDisplayBar = null!; - protected Container UnrankedBadge { get; private set; } = null!; + private Drawable unrankedBadge = null!; private ModDisplay modDisplay = null!; private OsuSpriteText modCountText = null!; @@ -59,58 +64,19 @@ namespace osu.Game.Screens.Select.FooterV2 [BackgroundDependencyLoader] private void load() { - const float bar_shear_width = 7f; - const float bar_height = 37f; - const float mod_display_portion = 0.65f; - - var barShear = new Vector2(bar_shear_width / bar_height, 0); - Text = "Mods"; Icon = FontAwesome.Solid.ExchangeAlt; AccentColour = colours.Lime1; AddRange(new[] { - UnrankedBadge = new ContainerWithTooltip - { - Margin = new MarginPadding { Left = BUTTON_WIDTH + 5f }, - Y = -5f, - Depth = float.MaxValue, - Origin = Anchor.BottomLeft, - Shear = barShear, - CornerRadius = CORNER_RADIUS, - AutoSizeAxes = Axes.X, - Height = bar_height, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2f, - TooltipText = ModSelectOverlayStrings.UnrankedExplanation, - Children = new Drawable[] - { - new Box - { - Colour = colours.Orange2, - RelativeSizeAxes = Axes.Both, - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shear = -barShear, - Text = ModSelectOverlayStrings.Unranked.ToUpper(), - Margin = new MarginPadding { Horizontal = 15 }, - UseFullGlyphHeight = false, - Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), - Colour = Color4.Black, - } - } - }, + unrankedBadge = new UnrankedBadge(), modDisplayBar = new Container { Y = -5f, Depth = float.MaxValue, Origin = Anchor.BottomLeft, - Shear = barShear, + Shear = bar_shear, CornerRadius = CORNER_RADIUS, Size = new Vector2(BUTTON_WIDTH, bar_height), Masking = true, @@ -140,7 +106,7 @@ namespace osu.Game.Screens.Select.FooterV2 { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -barShear, + Shear = -bar_shear, UseFullGlyphHeight = false, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold) } @@ -162,7 +128,7 @@ namespace osu.Game.Screens.Select.FooterV2 { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -barShear, + Shear = -bar_shear, Scale = new Vector2(0.6f), Current = { BindTarget = Current }, ExpansionMode = ExpansionMode.AlwaysContracted, @@ -171,7 +137,7 @@ namespace osu.Game.Screens.Select.FooterV2 { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -barShear, + Shear = -bar_shear, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), Mods = { BindTarget = Current }, } @@ -216,8 +182,8 @@ namespace osu.Game.Screens.Select.FooterV2 modDisplay.FadeOut(duration, easing); modCountText.FadeOut(duration, easing); - UnrankedBadge.MoveToY(20, duration, easing); - UnrankedBadge.FadeOut(duration, easing); + unrankedBadge.MoveToY(20, duration, easing); + unrankedBadge.FadeOut(duration, easing); // add delay to let unranked indicator hide first before resizing the button back to its original width. this.Delay(duration).ResizeWidthTo(BUTTON_WIDTH, duration, easing); @@ -234,21 +200,21 @@ namespace osu.Game.Screens.Select.FooterV2 if (Current.Value.Any(m => !m.Ranked)) { - UnrankedBadge.MoveToX(0, duration, easing); - UnrankedBadge.FadeIn(duration, easing); + unrankedBadge.MoveToX(0, duration, easing); + unrankedBadge.FadeIn(duration, easing); - this.ResizeWidthTo(BUTTON_WIDTH + 5 + UnrankedBadge.DrawWidth, duration, easing); + this.ResizeWidthTo(BUTTON_WIDTH + 5 + unrankedBadge.DrawWidth, duration, easing); } else { - UnrankedBadge.MoveToX(-UnrankedBadge.DrawWidth, duration, easing); - UnrankedBadge.FadeOut(duration, easing); + unrankedBadge.MoveToX(-unrankedBadge.DrawWidth, duration, easing); + unrankedBadge.FadeOut(duration, easing); this.ResizeWidthTo(BUTTON_WIDTH, duration, easing); } modDisplayBar.MoveToY(-5, duration, Easing.OutQuint); - UnrankedBadge.MoveToY(-5, duration, easing); + unrankedBadge.MoveToY(-5, duration, easing); modDisplayBar.FadeIn(duration, easing); } @@ -327,9 +293,49 @@ namespace osu.Game.Screens.Select.FooterV2 } } - private partial class ContainerWithTooltip : Container, IHasTooltip + internal partial class UnrankedBadge : CompositeDrawable, IHasTooltip { - public LocalisableString TooltipText { get; set; } + public LocalisableString TooltipText { get; } + + public UnrankedBadge() + { + Margin = new MarginPadding { Left = BUTTON_WIDTH + 5f }; + Y = -5f; + Depth = float.MaxValue; + Origin = Anchor.BottomLeft; + Shear = bar_shear; + CornerRadius = CORNER_RADIUS; + AutoSizeAxes = Axes.X; + Height = bar_height; + Masking = true; + BorderColour = Color4.White; + BorderThickness = 2f; + TooltipText = ModSelectOverlayStrings.UnrankedExplanation; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Orange2, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = -bar_shear, + Text = ModSelectOverlayStrings.Unranked.ToUpper(), + Margin = new MarginPadding { Horizontal = 15 }, + UseFullGlyphHeight = false, + Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), + Colour = Color4.Black, + } + }; + } } } } From 4cf6ab40f6534dc0a8f479b3d561b5a3bcd8b403 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 11 May 2024 22:33:18 +0300 Subject: [PATCH 444/581] Use `MustDisposeResource` annotation to appease ReSharper inspections --- osu.Game/Database/EmptyRealmSet.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Database/EmptyRealmSet.cs b/osu.Game/Database/EmptyRealmSet.cs index 02dfa50fe5..c34974cb03 100644 --- a/osu.Game/Database/EmptyRealmSet.cs +++ b/osu.Game/Database/EmptyRealmSet.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using JetBrains.Annotations; using Realms; using Realms.Schema; @@ -15,8 +16,12 @@ namespace osu.Game.Database { private IList emptySet => Array.Empty(); + [MustDisposeResource] public IEnumerator GetEnumerator() => emptySet.GetEnumerator(); + + [MustDisposeResource] IEnumerator IEnumerable.GetEnumerator() => emptySet.GetEnumerator(); + public int Count => emptySet.Count; public T this[int index] => emptySet[index]; public int IndexOf(object? item) => item == null ? -1 : emptySet.IndexOf((T)item); From 5e8f6df79952b26793c4bca8b74cfcdf1c0a90a3 Mon Sep 17 00:00:00 2001 From: Thomas Mok <42684333+tomm13@users.noreply.github.com> Date: Sun, 12 May 2024 01:55:18 +0100 Subject: [PATCH 445/581] Add glow to footerV2 buttons --- osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs index 2c841f6ae6..d856780dfe 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; @@ -68,6 +69,7 @@ namespace osu.Game.Screens.Select.FooterV2 protected Container TextContainer; private readonly Box bar; private readonly Box backgroundBox; + private readonly Box glowBox; public FooterButtonV2() { @@ -93,6 +95,10 @@ namespace osu.Game.Screens.Select.FooterV2 { RelativeSizeAxes = Axes.Both }, + glowBox = new Box + { + RelativeSizeAxes = Axes.Both + }, // For elements that should not be sheared. new Container { @@ -211,6 +217,7 @@ namespace osu.Game.Screens.Select.FooterV2 } backgroundBox.FadeColour(backgroundColour, transition_length, Easing.OutQuint); + glowBox.Colour = ColourInfo.GradientVertical(buttonAccentColour.Opacity(0f), buttonAccentColour.Opacity(0.2f)); } } } From fa6ccc854d5324d08baf4e6ee77ce7373d75ef0a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 12 May 2024 05:24:20 +0300 Subject: [PATCH 446/581] Revert behavioural changes on options button --- .../Select/FooterV2/BeatmapOptionsPopover.cs | 4 +-- .../Select/FooterV2/FooterButtonOptionsV2.cs | 31 +------------------ 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs b/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs index 648f536bb1..d98164c306 100644 --- a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs +++ b/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs @@ -188,9 +188,7 @@ namespace osu.Game.Screens.Select.FooterV2 protected override void UpdateState(ValueChangedEvent state) { base.UpdateState(state); - // intentionally scheduling to let the button have a chance whether the popover will hide from clicking the button or clicking outside - // see the "hidingFromClick" field in FooterButtonOptionsV2. - Schedule(() => footerButton.OverlayState.Value = state.NewValue); + footerButton.OverlayState.Value = state.NewValue; } } } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs b/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs index 2ed8480b46..555215056a 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs +++ b/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs @@ -3,11 +3,9 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Input.Bindings; @@ -15,11 +13,6 @@ namespace osu.Game.Screens.Select.FooterV2 { public partial class FooterButtonOptionsV2 : FooterButtonV2, IHasPopover { - /// - /// True if the next click is for hiding the popover. - /// - private bool hidingFromClick; - [BackgroundDependencyLoader] private void load(OsuColour colour) { @@ -28,29 +21,7 @@ namespace osu.Game.Screens.Select.FooterV2 AccentColour = colour.Purple1; Hotkey = GlobalAction.ToggleBeatmapOptions; - Action = () => - { - if (OverlayState.Value == Visibility.Hidden && !hidingFromClick) - this.ShowPopover(); - - hidingFromClick = false; - }; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (OverlayState.Value == Visibility.Visible) - hidingFromClick = true; - - return base.OnMouseDown(e); - } - - protected override void Flash() - { - if (hidingFromClick) - return; - - base.Flash(); + Action = this.ShowPopover; } public Popover GetPopover() => new BeatmapOptionsPopover(this); From 260c224619289efa6e6f603c8cd8584ad445eeed Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 14 May 2024 01:23:40 +0900 Subject: [PATCH 447/581] Add failing test --- .../TestSceneOsuTouchInput.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index 5bf7c0326a..d3711c0cc6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -19,6 +19,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Screens.Play.HUD; using osu.Game.Tests.Visual; @@ -578,6 +579,25 @@ namespace osu.Game.Rulesets.Osu.Tests assertKeyCounter(1, 1); } + [Test] + [Solo] + public void TestTouchJudgedCircle() + { + addHitCircleAt(TouchSource.Touch1); + addHitCircleAt(TouchSource.Touch2); + + beginTouch(TouchSource.Touch1); + endTouch(TouchSource.Touch1); + + // Hold the second touch (this becomes the primary touch). + beginTouch(TouchSource.Touch2); + + // Touch again on the first circle. + // Because it's been judged, the cursor should not move here. + beginTouch(TouchSource.Touch1); + checkPosition(TouchSource.Touch2); + } + private void addHitCircleAt(TouchSource source) { AddStep($"Add circle at {source}", () => @@ -590,6 +610,7 @@ namespace osu.Game.Rulesets.Osu.Tests { Clock = new FramedClock(new ManualClock()), Position = mainContent.ToLocalSpace(getSanePositionForSource(source)), + CheckHittable = (_, _, _) => ClickAction.Hit }); }); } From ff0c0d54c9127df3006bcb4249fea225d5cc3f6f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 14 May 2024 01:23:55 +0900 Subject: [PATCH 448/581] Fix taps on judged circles changing cursor position --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index ceac1989a6..65bd585e98 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu // // Based on user feedback of more nuanced scenarios (where touch doesn't behave as expected), // this can be expanded to a more complex implementation, but I'd still want to keep it as simple as we can. - NonPositionalInputQueue.OfType().Any(c => c.ReceivePositionalInputAt(screenSpacePosition)); + NonPositionalInputQueue.OfType().Any(c => c.CanBeHit() && c.ReceivePositionalInputAt(screenSpacePosition)); public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique) From 20e28964352c140bb71198c4be743b6189a5edfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 13 May 2024 18:48:08 +0200 Subject: [PATCH 449/581] Remove leftover `[Solo]` attribute --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs index d3711c0cc6..bf0ab8efa0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuTouchInput.cs @@ -580,7 +580,6 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] - [Solo] public void TestTouchJudgedCircle() { addHitCircleAt(TouchSource.Touch1); From cfb2c8272b1db1d774678d127a3b2ee70e264b7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 May 2024 22:56:15 +0800 Subject: [PATCH 450/581] Set a rudimentary lifetime end to improve seek performance in scrolling rulesets --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 4e72291b9c..e70e181a50 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // Scroll info is not available until loaded. // The lifetime of all entries will be updated in the first Update. if (IsLoaded) - setComputedLifetimeStart(entry); + setComputedLifetime(entry); base.Add(entry); } @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.UI.Scrolling layoutComputed.Clear(); foreach (var entry in Entries) - setComputedLifetimeStart(entry); + setComputedLifetime(entry); algorithm.Value.Reset(); @@ -234,12 +234,13 @@ namespace osu.Game.Rulesets.UI.Scrolling return algorithm.Value.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength); } - private void setComputedLifetimeStart(HitObjectLifetimeEntry entry) + private void setComputedLifetime(HitObjectLifetimeEntry entry) { double computedStartTime = computeDisplayStartTime(entry); // always load the hitobject before its first judgement offset entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - entry.HitObject.MaximumJudgementOffset, computedStartTime); + entry.LifetimeEnd = entry.HitObject.GetEndTime() + timeRange.Value; } private void updateLayoutRecursive(DrawableHitObject hitObject, double? parentHitObjectStartTime = null) @@ -261,7 +262,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime updatePosition(obj, hitObject.HitObject.StartTime, parentHitObjectStartTime); - setComputedLifetimeStart(obj.Entry); + setComputedLifetime(obj.Entry); } } From 7f3fde2a25d0d78277a6794cc48ec856691e689a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 11:13:06 +0200 Subject: [PATCH 451/581] Add failing test case --- .../Visual/Navigation/TestScenePresentScore.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 004d1de116..212783d047 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -145,6 +145,19 @@ namespace osu.Game.Tests.Visual.Navigation presentAndConfirm(secondImport, type); } + [Test] + public void TestPresentTwoImportsWithSameOnlineIDButDifferentHashes([Values] ScorePresentType type) + { + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); + AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); + + var firstImport = importScore(1); + presentAndConfirm(firstImport, type); + + var secondImport = importScore(1); + presentAndConfirm(secondImport, type); + } + private void returnToMenu() { // if we don't pause, there's a chance the track may change at the main menu out of our control (due to reaching the end of the track). From 03a279a48d476b2529d01d975022cb927eb80875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 11:14:46 +0200 Subject: [PATCH 452/581] Use hash rather than online ID as primary lookup key when presenting score Something I ran into when investigating https://github.com/ppy/osu/issues/28169. If there are two scores with the same online ID available in the database - for instance, one being recorded locally, and one recorded by spectator server, of one single play - the lookup code would use online ID first to find the score and pick any first one that matched. This could lead to the wrong replay being refetched and presented / exported. (In the case of the aforementioned issue, I was confused as to whether after restarting spectator server midway through a play and importing the replay saved by spectator server after the restart, I was seeing a complete replay with no dropped frames, even though there was nothing in the code that prevented the frame drop. It turns out that I was getting presented the locally recorded replay instead all along.) Instead, jiggle the fallback preference to use hash first. --- osu.Game/Scoring/ScoreManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 1ba5c7d4cf..0c707ffa19 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -88,15 +88,15 @@ namespace osu.Game.Scoring { ScoreInfo? databasedScoreInfo = null; + if (originalScoreInfo is ScoreInfo scoreInfo) + databasedScoreInfo = Query(s => s.Hash == scoreInfo.Hash); + if (originalScoreInfo.OnlineID > 0) - databasedScoreInfo = Query(s => s.OnlineID == originalScoreInfo.OnlineID); + databasedScoreInfo ??= Query(s => s.OnlineID == originalScoreInfo.OnlineID); if (originalScoreInfo.LegacyOnlineID > 0) databasedScoreInfo ??= Query(s => s.LegacyOnlineID == originalScoreInfo.LegacyOnlineID); - if (originalScoreInfo is ScoreInfo scoreInfo) - databasedScoreInfo ??= Query(s => s.Hash == scoreInfo.Hash); - if (databasedScoreInfo == null) { Logger.Log("The requested score could not be found locally.", LoggingTarget.Information); From 4f6777a0a12fe204a399a19bcc7a1fa54a369f2c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 16:00:18 +0900 Subject: [PATCH 453/581] Remove existing per-column touch input --- osu.Game.Rulesets.Mania/UI/Column.cs | 36 +--------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 6cd55bb099..c05a8f2a29 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -93,8 +93,7 @@ namespace osu.Game.Rulesets.Mania.UI // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements externally // (see `Stage.columnBackgrounds`). BackgroundContainer, - TopLevelContainer, - new ColumnTouchInputArea(this) + TopLevelContainer }; var background = new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground()) @@ -181,38 +180,5 @@ namespace osu.Game.Rulesets.Mania.UI public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) // This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border => DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos)); - - public partial class ColumnTouchInputArea : Drawable - { - private readonly Column column; - - [Resolved(canBeNull: true)] - private ManiaInputManager maniaInputManager { get; set; } - - private KeyBindingContainer keyBindingContainer; - - public ColumnTouchInputArea(Column column) - { - RelativeSizeAxes = Axes.Both; - - this.column = column; - } - - protected override void LoadComplete() - { - keyBindingContainer = maniaInputManager?.KeyBindingContainer; - } - - protected override bool OnTouchDown(TouchDownEvent e) - { - keyBindingContainer?.TriggerPressed(column.Action.Value); - return true; - } - - protected override void OnTouchUp(TouchUpEvent e) - { - keyBindingContainer?.TriggerReleased(column.Action.Value); - } - } } } From ef40197713009fd58c04ddc4f516a6cefd001ed8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 17:11:15 +0900 Subject: [PATCH 454/581] Add mania touch overlay Adjust default anchor/origin --- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 20 ++- .../UI/ManiaTouchInputOverlay.cs | 144 ++++++++++++++++++ 2 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index b3420c49f3..7610e48582 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Mania.Beatmaps; @@ -60,10 +61,23 @@ namespace osu.Game.Rulesets.Mania.UI throw new ArgumentException("Can't have zero or fewer stages."); GridContainer playfieldGrid; - AddInternal(playfieldGrid = new GridContainer + + RelativeSizeAxes = Axes.Y; + AutoSizeAxes = Axes.X; + + AddRangeInternal(new Drawable[] { - RelativeSizeAxes = Axes.Both, - Content = new[] { new Drawable[stageDefinitions.Count] } + playfieldGrid = new GridContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Content = new[] { new Drawable[stageDefinitions.Count] }, + ColumnDimensions = Enumerable.Range(0, stageDefinitions.Count).Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray() + }, + new ManiaTouchInputOverlay + { + RelativeSizeAxes = Axes.Both, + } }); var normalColumnAction = ManiaAction.Key1; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs new file mode 100644 index 0000000000..476461959d --- /dev/null +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs @@ -0,0 +1,144 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Configuration; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Mania.UI +{ + public partial class ManiaTouchInputOverlay : CompositeDrawable, ISerialisableDrawable + { + [SettingSource("Spacing", "The spacing between input receptors.")] + public BindableFloat Spacing { get; } = new BindableFloat(10) + { + Precision = 1, + MinValue = 0, + MaxValue = 100, + }; + + [Resolved] + private ManiaPlayfield playfield { get; set; } = null!; + + public ManiaTouchInputOverlay() + { + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + RelativeSizeAxes = Axes.Both; + Height = 0.5f; + } + + [BackgroundDependencyLoader] + private void load() + { + List receptorGridContent = new List(); + List receptorGridDimensions = new List(); + + bool first = true; + + foreach (var stage in playfield.Stages) + { + foreach (var column in stage.Columns) + { + if (!first) + { + receptorGridContent.Add(new Gutter { Spacing = { BindTarget = Spacing } }); + receptorGridDimensions.Add(new Dimension(GridSizeMode.AutoSize)); + } + + receptorGridContent.Add(new InputReceptor()); + receptorGridDimensions.Add(new Dimension()); + + first = false; + } + } + + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] { receptorGridContent.ToArray() }, + ColumnDimensions = receptorGridDimensions.ToArray() + }; + } + + public bool UsesFixedAnchor { get; set; } + + public partial class InputReceptor : CompositeDrawable + { + private readonly Box highlightOverlay; + + public InputReceptor() + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 10, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.15f, + }, + highlightOverlay = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Blending = BlendingParameters.Additive, + } + } + } + }; + } + + protected override bool OnTouchDown(TouchDownEvent e) + { + updateHighlight(true); + return true; + } + + protected override void OnTouchUp(TouchUpEvent e) + { + updateHighlight(false); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + updateHighlight(true); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + updateHighlight(false); + } + + private void updateHighlight(bool enabled) + { + highlightOverlay.FadeTo(enabled ? 0.1f : 0, enabled ? 80 : 400, Easing.OutQuint); + } + } + + private partial class Gutter : Drawable + { + public readonly IBindable Spacing = new Bindable(); + + public Gutter() + { + Spacing.BindValueChanged(s => Size = new Vector2(s.NewValue)); + } + } + } +} From 39337f5189fb371ba91c6b5145374213cbbfba71 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 17:25:05 +0900 Subject: [PATCH 455/581] Hook up input manager --- .../UI/ManiaTouchInputOverlay.cs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs index 476461959d..10de89e950 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.UI receptorGridDimensions.Add(new Dimension(GridSizeMode.AutoSize)); } - receptorGridContent.Add(new InputReceptor()); + receptorGridContent.Add(new InputReceptor { Action = { BindTarget = column.Action } }); receptorGridDimensions.Add(new Dimension()); first = false; @@ -72,8 +72,15 @@ namespace osu.Game.Rulesets.Mania.UI public partial class InputReceptor : CompositeDrawable { + public readonly IBindable Action = new Bindable(); + private readonly Box highlightOverlay; + [Resolved] + private ManiaInputManager? inputManager { get; set; } + + private bool isPressed; + public InputReceptor() { RelativeSizeAxes = Axes.Both; @@ -105,29 +112,43 @@ namespace osu.Game.Rulesets.Mania.UI protected override bool OnTouchDown(TouchDownEvent e) { - updateHighlight(true); + updateButton(true); return true; } protected override void OnTouchUp(TouchUpEvent e) { - updateHighlight(false); + updateButton(false); } protected override bool OnMouseDown(MouseDownEvent e) { - updateHighlight(true); + updateButton(true); return true; } protected override void OnMouseUp(MouseUpEvent e) { - updateHighlight(false); + updateButton(false); } - private void updateHighlight(bool enabled) + private void updateButton(bool press) { - highlightOverlay.FadeTo(enabled ? 0.1f : 0, enabled ? 80 : 400, Easing.OutQuint); + if (press == isPressed) + return; + + isPressed = press; + + if (press) + { + inputManager?.KeyBindingContainer?.TriggerPressed(Action.Value); + highlightOverlay.FadeTo(0.1f, 80, Easing.OutQuint); + } + else + { + inputManager?.KeyBindingContainer?.TriggerReleased(Action.Value); + highlightOverlay.FadeTo(0, 400, Easing.OutQuint); + } } } From e3f2e1ba08c79e01b9dc9a0d76ff9bf21f41c32e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 18:20:30 +0900 Subject: [PATCH 456/581] Add opacity setting --- .../UI/ManiaTouchInputOverlay.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs index 10de89e950..ddff064133 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.UI { public partial class ManiaTouchInputOverlay : CompositeDrawable, ISerialisableDrawable { - [SettingSource("Spacing", "The spacing between input receptors.")] + [SettingSource("Spacing", "The spacing between receptors.")] public BindableFloat Spacing { get; } = new BindableFloat(10) { Precision = 1, @@ -24,6 +24,14 @@ namespace osu.Game.Rulesets.Mania.UI MaxValue = 100, }; + [SettingSource("Opacity", "The receptor opacity.")] + public BindableFloat Opacity { get; } = new BindableFloat(1) + { + Precision = 0.1f, + MinValue = 0, + MaxValue = 1 + }; + [Resolved] private ManiaPlayfield playfield { get; set; } = null!; @@ -68,6 +76,12 @@ namespace osu.Game.Rulesets.Mania.UI }; } + protected override void LoadComplete() + { + base.LoadComplete(); + Opacity.BindValueChanged(o => Alpha = o.NewValue, true); + } + public bool UsesFixedAnchor { get; set; } public partial class InputReceptor : CompositeDrawable From a761a7bced2deacea3f7ee9c2e765c59e7cd0670 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 4 Apr 2024 18:20:51 +0900 Subject: [PATCH 457/581] Hook up touch device mod --- .../Mods/TestSceneModTouchDevice.cs | 64 +++++++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 8 +++ osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 14 +++- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs new file mode 100644 index 0000000000..4c5e4933ef --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Testing; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Mods +{ + public partial class TestSceneModTouchDevice : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + [Test] + public void TestOverlayVisibleWithMod() => CreateModTest(new ModTestData + { + Mod = new ModTouchDevice(), + Autoplay = false, + PassCondition = () => getSkinnableOverlay()?.IsPresent == true + }); + + [Test] + public void TestOverlayNotVisibleWithoutMod() => CreateModTest(new ModTestData + { + Autoplay = false, + PassCondition = () => getSkinnableOverlay()?.IsPresent == false + }); + + [Test] + public void TestPressReceptors() + { + CreateModTest(new ModTestData + { + Mod = new ModTouchDevice(), + Autoplay = false, + PassCondition = () => true + }); + + for (int i = 0; i < 4; i++) + { + int index = i; + + AddStep($"touch receptor {index}", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre))); + + AddAssert("action sent", + () => this.ChildrenOfType().SelectMany(m => m.KeyBindingContainer.PressedActions), + () => Does.Contain(getReceptor(index).Action.Value)); + + AddStep($"release receptor {index}", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre))); + } + } + + private Drawable? getSkinnableOverlay() => this.ChildrenOfType() + .SingleOrDefault(d => d.Lookup.Equals(new ManiaSkinComponentLookup(ManiaSkinComponents.TouchOverlay))); + + private ManiaTouchInputOverlay.InputReceptor getReceptor(int index) => this.ChildrenOfType().ElementAt(index); + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index b5614e2b56..23004e36a0 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -163,6 +163,9 @@ namespace osu.Game.Rulesets.Mania if (mods.HasFlagFast(LegacyMods.ScoreV2)) yield return new ModScoreV2(); + + if (mods.HasFlagFast(LegacyMods.TouchDevice)) + yield return new ModTouchDevice(); } public override LegacyMods ConvertToLegacyMods(Mod[] mods) @@ -225,6 +228,10 @@ namespace osu.Game.Rulesets.Mania case ManiaModRandom: value |= LegacyMods.Random; break; + + case ModTouchDevice: + value |= LegacyMods.TouchDevice; + break; } } @@ -296,6 +303,7 @@ namespace osu.Game.Rulesets.Mania case ModType.System: return new Mod[] { + new ModTouchDevice(), new ModScoreV2(), }; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 7610e48582..385a47f8b8 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Mania.UI private readonly List stages = new List(); + private readonly ManiaTouchInputOverlay touchOverlay; + public override Quad SkinnableComponentScreenSpaceDrawQuad { get @@ -74,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.UI Content = new[] { new Drawable[stageDefinitions.Count] }, ColumnDimensions = Enumerable.Range(0, stageDefinitions.Count).Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray() }, - new ManiaTouchInputOverlay + touchOverlay = new ManiaTouchInputOverlay { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both } }); @@ -97,6 +100,13 @@ namespace osu.Game.Rulesets.Mania.UI } } + protected override void LoadComplete() + { + base.LoadComplete(); + + touchOverlay.Alpha = Mods?.Any(m => m is ModTouchDevice) == true ? 1 : 0; + } + public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject); public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject); From cb49147d1e17d14c8b6a63c5a2e3b535a36f57a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 12:57:30 +0200 Subject: [PATCH 458/581] Apply NRT to `ScorePanelList` --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 ++ osu.Game/Screens/Ranking/ScorePanelList.cs | 19 +++++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 1c3518909d..44b270db53 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -329,6 +330,7 @@ namespace osu.Game.Screens.Ranking { if (state.NewValue == Visibility.Visible) { + Debug.Assert(SelectedScore.Value != null); // Detach the panel in its original location, and move into the desired location in the local container. var expandedPanel = ScorePanelList.GetPanelForScore(SelectedScore.Value); var screenSpacePos = expandedPanel.ScreenSpaceDrawQuad.TopLeft; diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 95c90e35a0..e711bed729 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; -using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -64,14 +61,14 @@ namespace osu.Game.Screens.Ranking /// /// An action to be invoked if a is clicked while in an expanded state. /// - public Action PostExpandAction; + public Action? PostExpandAction; - public readonly Bindable SelectedScore = new Bindable(); + public readonly Bindable SelectedScore = new Bindable(); private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); private readonly Flow flow; private readonly Scroll scroll; - private ScorePanel expandedPanel; + private ScorePanel? expandedPanel; /// /// Creates a new . @@ -174,7 +171,7 @@ namespace osu.Game.Screens.Ranking /// Brings a to the centre of the screen and expands it. /// /// The to present. - private void selectedScoreChanged(ValueChangedEvent score) + private void selectedScoreChanged(ValueChangedEvent score) { // avoid contracting panels unnecessarily when TriggerChange is fired manually. if (score.OldValue != null && !score.OldValue.Equals(score.NewValue)) @@ -317,7 +314,7 @@ namespace osu.Game.Screens.Ranking protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - loadCancellationSource?.Cancel(); + loadCancellationSource.Cancel(); } private partial class Flow : FillFlowContainer @@ -326,11 +323,9 @@ namespace osu.Game.Screens.Ranking public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => !s.Panel.Score.Equals(score)).Count(); - [CanBeNull] - public ScoreInfo GetPreviousScore(ScoreInfo score) => applySorting(Children).TakeWhile(s => !s.Panel.Score.Equals(score)).LastOrDefault()?.Panel.Score; + public ScoreInfo? GetPreviousScore(ScoreInfo score) => applySorting(Children).TakeWhile(s => !s.Panel.Score.Equals(score)).LastOrDefault()?.Panel.Score; - [CanBeNull] - public ScoreInfo GetNextScore(ScoreInfo score) => applySorting(Children).SkipWhile(s => !s.Panel.Score.Equals(score)).ElementAtOrDefault(1)?.Panel.Score; + public ScoreInfo? GetNextScore(ScoreInfo score) => applySorting(Children).SkipWhile(s => !s.Panel.Score.Equals(score)).ElementAtOrDefault(1)?.Panel.Score; private IEnumerable applySorting(IEnumerable drawables) => drawables.OfType() .OrderByDescending(GetLayoutPosition) From 10a8e84046ac4e8abb06e44ab112e2b3274bb2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:01:26 +0200 Subject: [PATCH 459/581] Apply NRT to `StatisticsPanel` --- .../Ranking/Statistics/StatisticsPanel.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs index 19bd0c4393..f9f5254bc2 100644 --- a/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/StatisticsPanel.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Linq; using System.Threading; @@ -28,19 +26,20 @@ namespace osu.Game.Screens.Ranking.Statistics { public const float SIDE_PADDING = 30; - public readonly Bindable Score = new Bindable(); + public readonly Bindable Score = new Bindable(); protected override bool StartHidden => true; [Resolved] - private BeatmapManager beatmapManager { get; set; } + private BeatmapManager beatmapManager { get; set; } = null!; private readonly Container content; private readonly LoadingSpinner spinner; private bool wasOpened; - private Sample popInSample; - private Sample popOutSample; + private Sample? popInSample; + private Sample? popOutSample; + private CancellationTokenSource? loadCancellation; public StatisticsPanel() { @@ -71,9 +70,7 @@ namespace osu.Game.Screens.Ranking.Statistics popOutSample = audio.Samples.Get(@"Results/statistics-panel-pop-out"); } - private CancellationTokenSource loadCancellation; - - private void populateStatistics(ValueChangedEvent score) + private void populateStatistics(ValueChangedEvent score) { loadCancellation?.Cancel(); loadCancellation = null; @@ -187,7 +184,7 @@ namespace osu.Game.Screens.Ranking.Statistics LoadComponentAsync(container, d => { - if (!Score.Value.Equals(newScore)) + if (Score.Value?.Equals(newScore) != true) return; spinner.Hide(); From 77a7f475ee25314cc7d68e612f8bfbf2beae2ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:03:46 +0200 Subject: [PATCH 460/581] Apply NRT to `ScorePanel` --- osu.Game/Screens/Ranking/ScorePanel.cs | 34 ++++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 1f7ba3692a..e283749e32 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; -using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -83,8 +80,7 @@ namespace osu.Game.Screens.Ranking private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535"); private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535"); - [CanBeNull] - public event Action StateChanged; + public event Action? StateChanged; /// /// The position of the score in the rankings. @@ -94,28 +90,30 @@ namespace osu.Game.Screens.Ranking /// /// An action to be invoked if this is clicked while in an expanded state. /// - public Action PostExpandAction; + public Action? PostExpandAction; public readonly ScoreInfo Score; [Resolved] - private OsuGameBase game { get; set; } + private OsuGameBase game { get; set; } = null!; - private AudioContainer audioContent; + private AudioContainer audioContent = null!; private bool displayWithFlair; - private Container topLayerContainer; - private Drawable topLayerBackground; - private Container topLayerContentContainer; - private Drawable topLayerContent; + private Container topLayerContainer = null!; + private Drawable topLayerBackground = null!; + private Container topLayerContentContainer = null!; + private Drawable? topLayerContent; - private Container middleLayerContainer; - private Drawable middleLayerBackground; - private Container middleLayerContentContainer; - private Drawable middleLayerContent; + private Container middleLayerContainer = null!; + private Drawable middleLayerBackground = null!; + private Container middleLayerContentContainer = null!; + private Drawable? middleLayerContent; - private DrawableSample samplePanelFocus; + private ScorePanelTrackingContainer? trackingContainer; + + private DrawableSample? samplePanelFocus; public ScorePanel(ScoreInfo score, bool isNewLocalScore = false) { @@ -334,8 +332,6 @@ namespace osu.Game.Screens.Ranking || topLayerContainer.ReceivePositionalInputAt(screenSpacePos) || middleLayerContainer.ReceivePositionalInputAt(screenSpacePos); - private ScorePanelTrackingContainer trackingContainer; - /// /// Creates a which this can reside inside. /// The will track the size of this . From 2f2257f6cec8254ca48432d6811a0cb823f0364c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:07:49 +0200 Subject: [PATCH 461/581] Apply NRT to `PerformanceBreakdownChart` --- .../Statistics/PerformanceBreakdownChart.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs b/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs index 8b13f0951c..b5eed2d12a 100644 --- a/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs +++ b/osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs @@ -1,13 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; @@ -31,16 +28,16 @@ namespace osu.Game.Screens.Ranking.Statistics private readonly ScoreInfo score; private readonly IBeatmap playableBeatmap; - private Drawable spinner; - private Drawable content; - private GridContainer chart; - private OsuSpriteText achievedPerformance; - private OsuSpriteText maximumPerformance; + private Drawable spinner = null!; + private Drawable content = null!; + private GridContainer chart = null!; + private OsuSpriteText achievedPerformance = null!; + private OsuSpriteText maximumPerformance = null!; private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } + private BeatmapDifficultyCache difficultyCache { get; set; } = null!; public PerformanceBreakdownChart(ScoreInfo score, IBeatmap playableBeatmap) { @@ -147,7 +144,7 @@ namespace osu.Game.Screens.Ranking.Statistics new PerformanceBreakdownCalculator(playableBeatmap, difficultyCache) .CalculateAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely()))); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely()!))); } private void setPerformanceValue(PerformanceBreakdown breakdown) @@ -189,8 +186,7 @@ namespace osu.Game.Screens.Ranking.Statistics maximumPerformance.Text = Math.Round(perfectAttribute.Value, MidpointRounding.AwayFromZero).ToLocalisableString(); } - [CanBeNull] - private Drawable[] createAttributeRow(PerformanceDisplayAttribute attribute, PerformanceDisplayAttribute perfectAttribute) + private Drawable[]? createAttributeRow(PerformanceDisplayAttribute attribute, PerformanceDisplayAttribute perfectAttribute) { // Don't display the attribute if its maximum is 0 // For example, flashlight bonus would be zero if flashlight mod isn't on @@ -239,7 +235,7 @@ namespace osu.Game.Screens.Ranking.Statistics protected override void Dispose(bool isDisposing) { - cancellationTokenSource?.Cancel(); + cancellationTokenSource.Cancel(); base.Dispose(isDisposing); } } From 237ae8b46a2c66af53a350e6972696d2cda79686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:09:57 +0200 Subject: [PATCH 462/581] Apply NRT to `SimpleStatisticsItem` --- osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs index 23ccc3d0b7..d8de1b07b5 100644 --- a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs +++ b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -61,7 +59,7 @@ namespace osu.Game.Screens.Ranking.Statistics /// public partial class SimpleStatisticItem : SimpleStatisticItem { - private TValue value; + private TValue value = default!; /// /// The statistic's value to be displayed. @@ -80,7 +78,7 @@ namespace osu.Game.Screens.Ranking.Statistics /// Used to convert to a text representation. /// Defaults to using . /// - protected virtual string DisplayValue(TValue value) => value.ToString(); + protected virtual string DisplayValue(TValue value) => value!.ToString() ?? string.Empty; public SimpleStatisticItem(string name) : base(name) From 8e16b57d09663ecfd310487bb58db523b533e4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:10:36 +0200 Subject: [PATCH 463/581] Apply NRT to `SimpleStatisticTable` --- .../Screens/Ranking/Statistics/SimpleStatisticTable.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs index 4abf0007a7..da79fdb12b 100644 --- a/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs +++ b/osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs @@ -1,12 +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 disable - using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -24,14 +21,14 @@ namespace osu.Game.Screens.Ranking.Statistics private readonly SimpleStatisticItem[] items; private readonly int columnCount; - private FillFlowContainer[] columns; + private FillFlowContainer[] columns = null!; /// /// Creates a statistic row for the supplied s. /// /// The number of columns to layout the into. /// The s to display in this row. - public SimpleStatisticTable(int columnCount, [ItemNotNull] IEnumerable items) + public SimpleStatisticTable(int columnCount, IEnumerable items) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(columnCount); From e7721b073cbd09651b48b9253e2d7d963339f5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:11:21 +0200 Subject: [PATCH 464/581] Apply NRT to `ContractedPanelTopContent` --- .../Screens/Ranking/Contracted/ContractedPanelTopContent.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs index 93bc7c41e1..06d127b972 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -16,7 +14,7 @@ namespace osu.Game.Screens.Ranking.Contracted { public readonly Bindable ScorePosition = new Bindable(); - private OsuSpriteText text; + private OsuSpriteText text = null!; public ContractedPanelTopContent() { From ced1c79490f61b6d203d3777c740139e1cd7ff51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:14:52 +0200 Subject: [PATCH 465/581] Apply NRT to `AccuracyCircle` --- .../Expanded/Accuracy/AccuracyCircle.cs | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index f04e4a6444..cebc54f490 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -93,17 +91,17 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private readonly ScoreInfo score; - private CircularProgress accuracyCircle; - private GradedCircles gradedCircles; - private Container badges; - private RankText rankText; + private CircularProgress accuracyCircle = null!; + private GradedCircles gradedCircles = null!; + private Container badges = null!; + private RankText rankText = null!; - private PoolableSkinnableSample scoreTickSound; - private PoolableSkinnableSample badgeTickSound; - private PoolableSkinnableSample badgeMaxSound; - private PoolableSkinnableSample swooshUpSound; - private PoolableSkinnableSample rankImpactSound; - private PoolableSkinnableSample rankApplauseSound; + private PoolableSkinnableSample? scoreTickSound; + private PoolableSkinnableSample? badgeTickSound; + private PoolableSkinnableSample? badgeMaxSound; + private PoolableSkinnableSample? swooshUpSound; + private PoolableSkinnableSample? rankImpactSound; + private PoolableSkinnableSample? rankApplauseSound; private readonly Bindable tickPlaybackRate = new Bindable(); @@ -119,7 +117,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private readonly bool withFlair; private readonly bool isFailedSDueToMisses; - private RankText failedSRankText; + private RankText failedSRankText = null!; public AccuracyCircle(ScoreInfo score, bool withFlair = false) { @@ -229,8 +227,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy this.Delay(swoosh_pre_delay).Schedule(() => { - swooshUpSound.VolumeTo(swoosh_volume); - swooshUpSound.Play(); + swooshUpSound!.VolumeTo(swoosh_volume); + swooshUpSound!.Play(); }); } @@ -287,8 +285,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_start); this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine); - scoreTickSound.FrequencyTo(1 + targetAccuracy, ACCURACY_TRANSFORM_DURATION, Easing.OutSine); - scoreTickSound.VolumeTo(score_tick_volume_start).Then().VolumeTo(score_tick_volume_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine); + scoreTickSound!.FrequencyTo(1 + targetAccuracy, ACCURACY_TRANSFORM_DURATION, Easing.OutSine); + scoreTickSound!.VolumeTo(score_tick_volume_start).Then().VolumeTo(score_tick_volume_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine); isTicking = true; }); @@ -314,8 +312,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy { var dink = badgeNum < badges.Count - 1 ? badgeTickSound : badgeMaxSound; - dink.FrequencyTo(1 + badgeNum++ * 0.05); - dink.Play(); + dink!.FrequencyTo(1 + badgeNum++ * 0.05); + dink!.Play(); }); } } @@ -331,7 +329,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Schedule(() => { isTicking = false; - rankImpactSound.Play(); + rankImpactSound!.Play(); }); const double applause_pre_delay = 545f; @@ -341,8 +339,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy { Schedule(() => { - rankApplauseSound.VolumeTo(applause_volume); - rankApplauseSound.Play(); + rankApplauseSound!.VolumeTo(applause_volume); + rankApplauseSound!.Play(); }); } } From b937b94bd2feb31995cbb2b0e0c0cc8126195b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:15:21 +0200 Subject: [PATCH 466/581] Apply NRT to `RankBadge` --- osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs index 8aea6045eb..0e798c7d6e 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankBadge.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -34,8 +32,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy public readonly ScoreRank Rank; - private Drawable rankContainer; - private Drawable overlay; + private Drawable rankContainer = null!; + private Drawable overlay = null!; /// /// Creates a new . From 414f023817027d0433d99b921177e5f98b0ff2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:15:51 +0200 Subject: [PATCH 467/581] Apply NRT to `RankText` --- osu.Game/Screens/Ranking/Expanded/Accuracy/RankText.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankText.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankText.cs index b7adcb032f..76e59b32b8 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/RankText.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/RankText.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -23,9 +21,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy { private readonly ScoreRank rank; - private BufferedContainer flash; - private BufferedContainer superFlash; - private GlowingSpriteText rankText; + private BufferedContainer flash = null!; + private BufferedContainer superFlash = null!; + private GlowingSpriteText rankText = null!; public RankText(ScoreRank rank) { From ee9144c3bddd0b16b7bd18dd51222bfdadec5f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 13:17:31 +0200 Subject: [PATCH 468/581] Apply NRT to results statistics displays --- .../Ranking/Expanded/Statistics/AccuracyStatistic.cs | 4 +--- .../Screens/Ranking/Expanded/Statistics/ComboStatistic.cs | 4 +--- .../Screens/Ranking/Expanded/Statistics/CounterStatistic.cs | 4 +--- .../Ranking/Expanded/Statistics/PerformanceStatistic.cs | 6 ++---- .../Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs | 6 ++---- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index f1f2c47e20..a4672a475c 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -22,7 +20,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { private readonly double accuracy; - private RollingCounter counter; + private RollingCounter counter = null!; /// /// Creates a new . diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs index 6290cee6da..7c91a37b77 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/ComboStatistic.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -22,7 +20,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics { private readonly bool isPerfect; - private Drawable perfectText; + private Drawable perfectText = null!; /// /// Creates a new . diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs index 8528dac83b..4042724c75 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/CounterStatistic.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -21,7 +19,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics private readonly int count; private readonly int? maxCount; - private RollingCounter counter; + private RollingCounter counter = null!; /// /// Creates a new . diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 8366f8d7ef..7ea3cbe917 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -32,7 +30,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - private RollingCounter counter; + private RollingCounter counter = null!; public PerformanceStatistic(ScoreInfo score) : base(BeatmapsetsStrings.ShowScoreboardHeaderspp) @@ -107,7 +105,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics protected override void Dispose(bool isDisposing) { - cancellationTokenSource?.Cancel(); + cancellationTokenSource.Cancel(); base.Dispose(isDisposing); } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs index 686b6c7d47..9de60f013d 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/StatisticDisplay.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.LocalisationExtensions; @@ -21,10 +19,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics /// public abstract partial class StatisticDisplay : CompositeDrawable { - protected SpriteText HeaderText { get; private set; } + protected SpriteText HeaderText { get; private set; } = null!; private readonly LocalisableString header; - private Drawable content; + private Drawable content = null!; /// /// Creates a new . From 12e98fe55df37690f24de1450c1f59a9e75dd274 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 May 2024 15:24:32 +0800 Subject: [PATCH 469/581] Move out of playfield so touch overlay is not affected by playfield position --- .../UI/DrawableManiaRuleset.cs | 10 ++++++- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 30 ++----------------- .../UI/ManiaTouchInputOverlay.cs | 5 ++-- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 275b1311de..a948117748 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -31,6 +31,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.UI { + [Cached] public partial class DrawableManiaRuleset : DrawableScrollingRuleset { /// @@ -43,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.UI /// public const double MAX_TIME_RANGE = 11485; - protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield; + public new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield; public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; @@ -103,6 +104,11 @@ namespace osu.Game.Rulesets.Mania.UI configScrollSpeed.BindValueChanged(speed => this.TransformTo(nameof(smoothTimeRange), ComputeScrollTime(speed.NewValue), 200, Easing.OutQuint)); TimeRange.Value = smoothTimeRange = ComputeScrollTime(configScrollSpeed.Value); + + KeyBindingInputManager.Add(touchOverlay = new ManiaTouchInputOverlay + { + RelativeSizeAxes = Axes.Both + }); } protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount; @@ -116,6 +122,8 @@ namespace osu.Game.Rulesets.Mania.UI private ScheduledDelegate? pendingSkinChange; private float hitPosition; + private ManiaTouchInputOverlay touchOverlay = null!; + private void onSkinChange() { // schedule required to avoid calls after disposed. diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 385a47f8b8..b3420c49f3 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -7,12 +7,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -27,8 +25,6 @@ namespace osu.Game.Rulesets.Mania.UI private readonly List stages = new List(); - private readonly ManiaTouchInputOverlay touchOverlay; - public override Quad SkinnableComponentScreenSpaceDrawQuad { get @@ -64,23 +60,10 @@ namespace osu.Game.Rulesets.Mania.UI throw new ArgumentException("Can't have zero or fewer stages."); GridContainer playfieldGrid; - - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - - AddRangeInternal(new Drawable[] + AddInternal(playfieldGrid = new GridContainer { - playfieldGrid = new GridContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Content = new[] { new Drawable[stageDefinitions.Count] }, - ColumnDimensions = Enumerable.Range(0, stageDefinitions.Count).Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray() - }, - touchOverlay = new ManiaTouchInputOverlay - { - RelativeSizeAxes = Axes.Both - } + RelativeSizeAxes = Axes.Both, + Content = new[] { new Drawable[stageDefinitions.Count] } }); var normalColumnAction = ManiaAction.Key1; @@ -100,13 +83,6 @@ namespace osu.Game.Rulesets.Mania.UI } } - protected override void LoadComplete() - { - base.LoadComplete(); - - touchOverlay.Alpha = Mods?.Any(m => m is ModTouchDevice) == true ? 1 : 0; - } - public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject); public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs index ddff064133..a51a3a605b 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs @@ -33,12 +33,13 @@ namespace osu.Game.Rulesets.Mania.UI }; [Resolved] - private ManiaPlayfield playfield { get; set; } = null!; + private DrawableManiaRuleset drawableRuleset { get; set; } = null!; public ManiaTouchInputOverlay() { Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; + RelativeSizeAxes = Axes.Both; Height = 0.5f; } @@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.UI bool first = true; - foreach (var stage in playfield.Stages) + foreach (var stage in drawableRuleset.Playfield.Stages) { foreach (var column in stage.Columns) { From 390557634a76d457d3412d207efe68417bcd7773 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 May 2024 19:16:07 +0800 Subject: [PATCH 470/581] Rename touch area class to match existing usage (see taiko) --- osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 4 ++-- .../UI/{ManiaTouchInputOverlay.cs => ManiaTouchInputArea.cs} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game.Rulesets.Mania/UI/{ManiaTouchInputOverlay.cs => ManiaTouchInputArea.cs} (97%) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs index 4c5e4933ef..829cd0b62e 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs @@ -59,6 +59,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods private Drawable? getSkinnableOverlay() => this.ChildrenOfType() .SingleOrDefault(d => d.Lookup.Equals(new ManiaSkinComponentLookup(ManiaSkinComponents.TouchOverlay))); - private ManiaTouchInputOverlay.InputReceptor getReceptor(int index) => this.ChildrenOfType().ElementAt(index); + private ManiaTouchInputArea.InputReceptor getReceptor(int index) => this.ChildrenOfType().ElementAt(index); } } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index a948117748..5974b76d65 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.UI TimeRange.Value = smoothTimeRange = ComputeScrollTime(configScrollSpeed.Value); - KeyBindingInputManager.Add(touchOverlay = new ManiaTouchInputOverlay + KeyBindingInputManager.Add(touchArea = new ManiaTouchInputArea { RelativeSizeAxes = Axes.Both }); @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Mania.UI private ScheduledDelegate? pendingSkinChange; private float hitPosition; - private ManiaTouchInputOverlay touchOverlay = null!; + private ManiaTouchInputArea touchArea = null!; private void onSkinChange() { diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs similarity index 97% rename from osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs rename to osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs index a51a3a605b..0cb12128e8 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputOverlay.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Rulesets.Mania.UI { - public partial class ManiaTouchInputOverlay : CompositeDrawable, ISerialisableDrawable + public partial class ManiaTouchInputArea : CompositeDrawable, ISerialisableDrawable { [SettingSource("Spacing", "The spacing between receptors.")] public BindableFloat Spacing { get; } = new BindableFloat(10) @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.UI [Resolved] private DrawableManiaRuleset drawableRuleset { get; set; } = null!; - public ManiaTouchInputOverlay() + public ManiaTouchInputArea() { Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; From 5c9a90cb40320cf2f22e4b6ba9f94a7ab0d4e632 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 May 2024 19:28:14 +0800 Subject: [PATCH 471/581] Tidy class and change to be a `VisibilityContainer` similar to taiko implementation --- .../Mods/TestSceneModTouchDevice.cs | 11 ++-- .../UI/DrawableManiaRuleset.cs | 7 +-- .../UI/ManiaTouchInputArea.cs | 54 +++++++++++++++---- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs index 829cd0b62e..451cb617ee 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs @@ -3,12 +3,10 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; -using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests.Mods @@ -22,14 +20,14 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods { Mod = new ModTouchDevice(), Autoplay = false, - PassCondition = () => getSkinnableOverlay()?.IsPresent == true + PassCondition = () => getTouchOverlay()?.IsPresent == true }); [Test] public void TestOverlayNotVisibleWithoutMod() => CreateModTest(new ModTestData { Autoplay = false, - PassCondition = () => getSkinnableOverlay()?.IsPresent == false + PassCondition = () => getTouchOverlay()?.IsPresent == false }); [Test] @@ -56,9 +54,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods } } - private Drawable? getSkinnableOverlay() => this.ChildrenOfType() - .SingleOrDefault(d => d.Lookup.Equals(new ManiaSkinComponentLookup(ManiaSkinComponents.TouchOverlay))); + private ManiaTouchInputArea? getTouchOverlay() => this.ChildrenOfType().SingleOrDefault(); - private ManiaTouchInputArea.InputReceptor getReceptor(int index) => this.ChildrenOfType().ElementAt(index); + private ManiaTouchInputArea.ColumnInputReceptor getReceptor(int index) => this.ChildrenOfType().ElementAt(index); } } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 5974b76d65..ce53862c76 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -105,10 +105,7 @@ namespace osu.Game.Rulesets.Mania.UI TimeRange.Value = smoothTimeRange = ComputeScrollTime(configScrollSpeed.Value); - KeyBindingInputManager.Add(touchArea = new ManiaTouchInputArea - { - RelativeSizeAxes = Axes.Both - }); + KeyBindingInputManager.Add(new ManiaTouchInputArea()); } protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount; @@ -122,8 +119,6 @@ namespace osu.Game.Rulesets.Mania.UI private ScheduledDelegate? pendingSkinChange; private float hitPosition; - private ManiaTouchInputArea touchArea = null!; - private void onSkinChange() { // schedule required to avoid calls after disposed. diff --git a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs index 0cb12128e8..32e4616a25 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs @@ -9,13 +9,19 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Configuration; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Mania.UI { - public partial class ManiaTouchInputArea : CompositeDrawable, ISerialisableDrawable + /// + /// An overlay that captures and displays osu!mania mouse and touch input. + /// + public partial class ManiaTouchInputArea : VisibilityContainer { + // visibility state affects our child. we always want to handle input. + public override bool PropagatePositionalInputSubTree => true; + public override bool PropagateNonPositionalInputSubTree => true; + [SettingSource("Spacing", "The spacing between receptors.")] public BindableFloat Spacing { get; } = new BindableFloat(10) { @@ -35,6 +41,8 @@ namespace osu.Game.Rulesets.Mania.UI [Resolved] private DrawableManiaRuleset drawableRuleset { get; set; } = null!; + private GridContainer gridContainer = null!; + public ManiaTouchInputArea() { Anchor = Anchor.BottomCentre; @@ -62,16 +70,17 @@ namespace osu.Game.Rulesets.Mania.UI receptorGridDimensions.Add(new Dimension(GridSizeMode.AutoSize)); } - receptorGridContent.Add(new InputReceptor { Action = { BindTarget = column.Action } }); + receptorGridContent.Add(new ColumnInputReceptor { Action = { BindTarget = column.Action } }); receptorGridDimensions.Add(new Dimension()); first = false; } } - InternalChild = new GridContainer + InternalChild = gridContainer = new GridContainer { RelativeSizeAxes = Axes.Both, + AlwaysPresent = true, Content = new[] { receptorGridContent.ToArray() }, ColumnDimensions = receptorGridDimensions.ToArray() }; @@ -83,9 +92,36 @@ namespace osu.Game.Rulesets.Mania.UI Opacity.BindValueChanged(o => Alpha = o.NewValue, true); } - public bool UsesFixedAnchor { get; set; } + protected override bool OnKeyDown(KeyDownEvent e) + { + // Hide whenever the keyboard is used. + Hide(); + return false; + } - public partial class InputReceptor : CompositeDrawable + protected override bool OnMouseDown(MouseDownEvent e) + { + Show(); + return true; + } + + protected override bool OnTouchDown(TouchDownEvent e) + { + Show(); + return true; + } + + protected override void PopIn() + { + gridContainer.FadeIn(500, Easing.OutQuint); + } + + protected override void PopOut() + { + gridContainer.FadeOut(300); + } + + public partial class ColumnInputReceptor : CompositeDrawable { public readonly IBindable Action = new Bindable(); @@ -96,7 +132,7 @@ namespace osu.Game.Rulesets.Mania.UI private bool isPressed; - public InputReceptor() + public ColumnInputReceptor() { RelativeSizeAxes = Axes.Both; @@ -128,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override bool OnTouchDown(TouchDownEvent e) { updateButton(true); - return true; + return false; // handled by parent container to show overlay. } protected override void OnTouchUp(TouchUpEvent e) @@ -139,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override bool OnMouseDown(MouseDownEvent e) { updateButton(true); - return true; + return false; // handled by parent container to show overlay. } protected override void OnMouseUp(MouseUpEvent e) From 636e2004711fe91474574a0acdc1ebd3519e8cc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 May 2024 21:56:00 +0800 Subject: [PATCH 472/581] Update tests in line with new structure --- ...ice.cs => TestSceneManiaTouchInputArea.cs} | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) rename osu.Game.Rulesets.Mania.Tests/{Mods/TestSceneModTouchDevice.cs => TestSceneManiaTouchInputArea.cs} (64%) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaTouchInputArea.cs similarity index 64% rename from osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs rename to osu.Game.Rulesets.Mania.Tests/TestSceneManiaTouchInputArea.cs index 451cb617ee..30c0113bff 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneModTouchDevice.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneManiaTouchInputArea.cs @@ -3,42 +3,28 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; using osu.Game.Tests.Visual; -namespace osu.Game.Rulesets.Mania.Tests.Mods +namespace osu.Game.Rulesets.Mania.Tests { - public partial class TestSceneModTouchDevice : ModTestScene + public partial class TestSceneManiaTouchInputArea : PlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); [Test] - public void TestOverlayVisibleWithMod() => CreateModTest(new ModTestData + public void TestTouchAreaNotInitiallyVisible() { - Mod = new ModTouchDevice(), - Autoplay = false, - PassCondition = () => getTouchOverlay()?.IsPresent == true - }); - - [Test] - public void TestOverlayNotVisibleWithoutMod() => CreateModTest(new ModTestData - { - Autoplay = false, - PassCondition = () => getTouchOverlay()?.IsPresent == false - }); + AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden); + } [Test] public void TestPressReceptors() { - CreateModTest(new ModTestData - { - Mod = new ModTouchDevice(), - Autoplay = false, - PassCondition = () => true - }); + AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden); for (int i = 0; i < 4; i++) { @@ -51,6 +37,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods () => Does.Contain(getReceptor(index).Action.Value)); AddStep($"release receptor {index}", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre))); + + AddAssert("touch area visible", () => getTouchOverlay()?.State.Value == Visibility.Visible); } } From f781dc3300267bf99d57b5331aa4c3ed4a48d7a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 May 2024 22:38:31 +0800 Subject: [PATCH 473/581] Remove touch mod addition to mania Feels a bit pointless? I dunno. --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 23004e36a0..b5614e2b56 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -163,9 +163,6 @@ namespace osu.Game.Rulesets.Mania if (mods.HasFlagFast(LegacyMods.ScoreV2)) yield return new ModScoreV2(); - - if (mods.HasFlagFast(LegacyMods.TouchDevice)) - yield return new ModTouchDevice(); } public override LegacyMods ConvertToLegacyMods(Mod[] mods) @@ -228,10 +225,6 @@ namespace osu.Game.Rulesets.Mania case ManiaModRandom: value |= LegacyMods.Random; break; - - case ModTouchDevice: - value |= LegacyMods.TouchDevice; - break; } } @@ -303,7 +296,6 @@ namespace osu.Game.Rulesets.Mania case ModType.System: return new Mod[] { - new ModTouchDevice(), new ModScoreV2(), }; From cff865b556399e1b15206d4d080147d473277f84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 May 2024 23:54:07 +0800 Subject: [PATCH 474/581] Continue loading even when osu! logo is being dragged at loading screen Closes https://github.com/ppy/osu/issues/28130. --- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 4f7e21dddf..51a0c94ff0 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play // not ready if the user is hovering one of the panes (logo is excluded), unless they are idle. (IsHovered || osuLogo?.IsHovered == true || idleTracker.IsIdle.Value) // not ready if the user is dragging a slider or otherwise. - && inputManager.DraggedDrawable == null + && (inputManager.DraggedDrawable == null || inputManager.DraggedDrawable is OsuLogo) // not ready if a focused overlay is visible, like settings. && inputManager.FocusedDrawable == null; From 3d190f7e88ff4beb32addcef83e20f2087de0061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 14 May 2024 18:41:15 +0200 Subject: [PATCH 475/581] Remove redundant cast --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 967cdb0e54..c229039dc3 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Edit { } - public new ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield); + public new ManiaPlayfield Playfield => drawableRuleset.Playfield; public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo; From a3960bf7155f6019dd552783b2266f7896df2d34 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 15 May 2024 14:17:28 +0800 Subject: [PATCH 476/581] Add inline comment explaining `LifetimeEnd` set for future visitors --- .../Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index e70e181a50..7841e65935 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -240,6 +240,13 @@ namespace osu.Game.Rulesets.UI.Scrolling // always load the hitobject before its first judgement offset entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - entry.HitObject.MaximumJudgementOffset, computedStartTime); + + // This is likely not entirely correct, but sets a sane expectation of the ending lifetime. + // A more correct lifetime will be overwritten after a DrawableHitObject is assigned via DrawableHitObject.updateState. + // + // It is required that we set a lifetime end here to ensure that in scenarios like loading a Player instance to a seeked + // location in a beatmap doesn't churn every hit object into a DrawableHitObject. Even in a pooled scenario, the overhead + // of this can be quite crippling. entry.LifetimeEnd = entry.HitObject.GetEndTime() + timeRange.Value; } From c4ac6d20a09b5704dd484b633142f517b527e2c2 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 15 May 2024 23:40:51 +0200 Subject: [PATCH 477/581] fix code quality --- .../Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index a1f6a1732a..c188d23a58 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -12,9 +12,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { public partial class SelectionBoxScaleHandle : SelectionBoxDragHandle { - [Resolved] - private SelectionBox selectionBox { get; set; } = null!; - [Resolved] private SelectionScaleHandler? scaleHandler { get; set; } From 21f5d891bb28a2edd835b4d4a2e69895b5ecf5dd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 04:36:14 +0300 Subject: [PATCH 478/581] Rename and move footer classes to appropriate places --- .../TestSceneScreenFooter.cs} | 25 ++++++++++--------- ....cs => TestSceneScreenFooterButtonMods.cs} | 16 ++++++------ .../FooterV2.cs => Footer/ScreenFooter.cs} | 12 ++++----- .../ScreenFooterButton.cs} | 6 ++--- .../Footer}/BeatmapOptionsPopover.cs | 7 +++--- .../Footer/ScreenFooterButtonMods.cs} | 5 ++-- .../Footer/ScreenFooterButtonOptions.cs} | 5 ++-- .../Footer/ScreenFooterButtonRandom.cs} | 5 ++-- 8 files changed, 43 insertions(+), 38 deletions(-) rename osu.Game.Tests/Visual/{SongSelect/TestSceneSongSelectFooterV2.cs => UserInterface/TestSceneScreenFooter.cs} (89%) rename osu.Game.Tests/Visual/UserInterface/{TestSceneFooterButtonModsV2.cs => TestSceneScreenFooterButtonMods.cs} (90%) rename osu.Game/Screens/{Select/FooterV2/FooterV2.cs => Footer/ScreenFooter.cs} (86%) rename osu.Game/Screens/{Select/FooterV2/FooterButtonV2.cs => Footer/ScreenFooterButton.cs} (97%) rename osu.Game/Screens/{Select/FooterV2 => SelectV2/Footer}/BeatmapOptionsPopover.cs (96%) rename osu.Game/Screens/{Select/FooterV2/FooterButtonModsV2.cs => SelectV2/Footer/ScreenFooterButtonMods.cs} (98%) rename osu.Game/Screens/{Select/FooterV2/FooterButtonOptionsV2.cs => SelectV2/Footer/ScreenFooterButtonOptions.cs} (83%) rename osu.Game/Screens/{Select/FooterV2/FooterButtonRandomV2.cs => SelectV2/Footer/ScreenFooterButtonRandom.cs} (97%) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs similarity index 89% rename from osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs index 93402e42ce..162609df70 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs @@ -15,15 +15,16 @@ using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Select.FooterV2; +using osu.Game.Screens.Footer; +using osu.Game.Screens.SelectV2.Footer; using osuTK.Input; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.UserInterface { - public partial class TestSceneSongSelectFooterV2 : OsuManualInputManagerTestScene + public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene { - private FooterButtonRandomV2 randomButton = null!; - private FooterButtonModsV2 modsButton = null!; + private ScreenFooterButtonRandom randomButton = null!; + private ScreenFooterButtonMods modsButton = null!; private bool nextRandomCalled; private bool previousRandomCalled; @@ -39,25 +40,25 @@ namespace osu.Game.Tests.Visual.SongSelect nextRandomCalled = false; previousRandomCalled = false; - FooterV2 footer; + ScreenFooter footer; Children = new Drawable[] { new PopoverContainer { RelativeSizeAxes = Axes.Both, - Child = footer = new FooterV2(), + Child = footer = new ScreenFooter(), }, overlay = new DummyOverlay() }; - footer.AddButton(modsButton = new FooterButtonModsV2 { Current = SelectedMods }, overlay); - footer.AddButton(randomButton = new FooterButtonRandomV2 + footer.AddButton(modsButton = new ScreenFooterButtonMods { Current = SelectedMods }, overlay); + footer.AddButton(randomButton = new ScreenFooterButtonRandom { NextRandom = () => nextRandomCalled = true, PreviousRandom = () => previousRandomCalled = true }); - footer.AddButton(new FooterButtonOptionsV2()); + footer.AddButton(new ScreenFooterButtonOptions()); overlay.Hide(); }); @@ -98,7 +99,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddStep("enable options", () => { - var optionsButton = this.ChildrenOfType().Last(); + var optionsButton = this.ChildrenOfType().Last(); optionsButton.Enabled.Value = true; optionsButton.TriggerClick(); @@ -108,7 +109,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestState() { - AddToggleStep("set options enabled state", state => this.ChildrenOfType().Last().Enabled.Value = state); + AddToggleStep("set options enabled state", state => this.ChildrenOfType().Last().Enabled.Value = state); } [Test] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs similarity index 90% rename from osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs index 4aca9dde3d..df2109ace8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonModsV2.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs @@ -12,21 +12,21 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Select.FooterV2; +using osu.Game.Screens.SelectV2.Footer; using osu.Game.Utils; namespace osu.Game.Tests.Visual.UserInterface { - public partial class TestSceneFooterButtonModsV2 : OsuTestScene + public partial class TestSceneScreenFooterButtonMods : OsuTestScene { - private readonly TestFooterButtonModsV2 footerButtonMods; + private readonly TestScreenFooterButtonMods footerButtonMods; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); - public TestSceneFooterButtonModsV2() + public TestSceneScreenFooterButtonMods() { - Add(footerButtonMods = new TestFooterButtonModsV2 + Add(footerButtonMods = new TestScreenFooterButtonMods { Anchor = Anchor.Centre, Origin = Anchor.CentreLeft, @@ -97,9 +97,9 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestUnrankedBadge() { AddStep(@"Add unranked mod", () => changeMods(new[] { new OsuModDeflate() })); - AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 1); + AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 1); AddStep(@"Clear selected mod", () => changeMods(Array.Empty())); - AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 0); + AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 0); } private void changeMods(IReadOnlyList mods) => footerButtonMods.Current.Value = mods; @@ -112,7 +112,7 @@ namespace osu.Game.Tests.Visual.UserInterface return expectedValue == footerButtonMods.MultiplierText.Current.Value; } - private partial class TestFooterButtonModsV2 : FooterButtonModsV2 + private partial class TestScreenFooterButtonMods : ScreenFooterButtonMods { public new OsuSpriteText MultiplierText => base.MultiplierText; } diff --git a/osu.Game/Screens/Select/FooterV2/FooterV2.cs b/osu.Game/Screens/Footer/ScreenFooter.cs similarity index 86% rename from osu.Game/Screens/Select/FooterV2/FooterV2.cs rename to osu.Game/Screens/Footer/ScreenFooter.cs index 370c28e2a5..01013bb466 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterV2.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -11,9 +11,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.Footer { - public partial class FooterV2 : InputBlockingContainer + public partial class ScreenFooter : InputBlockingContainer { //Should be 60, setting to 50 for now for the sake of matching the current BackButton height. private const int height = 50; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Select.FooterV2 /// The button to be added. /// The to be toggled by this button. - public void AddButton(FooterButtonV2 button, OverlayContainer? overlay = null) + public void AddButton(ScreenFooterButton button, OverlayContainer? overlay = null) { if (overlay != null) { @@ -46,9 +46,9 @@ namespace osu.Game.Screens.Select.FooterV2 } } - private FillFlowContainer buttons = null!; + private FillFlowContainer buttons = null!; - public FooterV2() + public ScreenFooter() { RelativeSizeAxes = Axes.X; Height = height; @@ -66,7 +66,7 @@ namespace osu.Game.Screens.Select.FooterV2 RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5 }, - buttons = new FillFlowContainer + buttons = new FillFlowContainer { Position = new Vector2(TwoLayerButton.SIZE_EXTENDED.X + padding, 10), Anchor = Anchor.BottomLeft, diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs similarity index 97% rename from osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs rename to osu.Game/Screens/Footer/ScreenFooterButton.cs index 80103242f8..b3b3c9a8ec 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonV2.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -21,9 +21,9 @@ using osu.Game.Overlays; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.Footer { - public partial class FooterButtonV2 : OsuClickableContainer, IKeyBindingHandler + public partial class ScreenFooterButton : OsuClickableContainer, IKeyBindingHandler { // This should be 12 by design, but an extra allowance is added due to the corner radius specification. private const float shear_width = 13.5f; @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Select.FooterV2 private readonly Box glowBox; private readonly Box flashLayer; - public FooterButtonV2() + public ScreenFooterButton() { Size = new Vector2(BUTTON_WIDTH, BUTTON_HEIGHT); diff --git a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs b/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs similarity index 96% rename from osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs rename to osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs index d98164c306..f73be15a36 100644 --- a/osu.Game/Screens/Select/FooterV2/BeatmapOptionsPopover.cs +++ b/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs @@ -20,24 +20,25 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Overlays; +using osu.Game.Screens.Select; using osuTK; using osuTK.Graphics; using osuTK.Input; using WebCommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.SelectV2.Footer { public partial class BeatmapOptionsPopover : OsuPopover { private FillFlowContainer buttonFlow = null!; - private readonly FooterButtonOptionsV2 footerButton; + private readonly ScreenFooterButtonOptions footerButton; private WorkingBeatmap beatmapWhenOpening = null!; [Resolved] private IBindable beatmap { get; set; } = null!; - public BeatmapOptionsPopover(FooterButtonOptionsV2 footerButton) + public BeatmapOptionsPopover(ScreenFooterButtonOptions footerButton) { this.footerButton = footerButton; } diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs similarity index 98% rename from osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs rename to osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs index 44db49b927..49961c60f8 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonModsV2.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs @@ -21,14 +21,15 @@ using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Footer; using osu.Game.Screens.Play.HUD; using osu.Game.Utils; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.SelectV2.Footer { - public partial class FooterButtonModsV2 : FooterButtonV2, IHasCurrentValue> + public partial class ScreenFooterButtonMods : ScreenFooterButton, IHasCurrentValue> { // todo: see https://github.com/ppy/osu-framework/issues/3271 private const float torus_scale_factor = 1.2f; diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs similarity index 83% rename from osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs rename to osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs index 555215056a..74fe3e3d11 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonOptionsV2.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs @@ -8,10 +8,11 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Screens.Footer; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.SelectV2.Footer { - public partial class FooterButtonOptionsV2 : FooterButtonV2, IHasPopover + public partial class ScreenFooterButtonOptions : ScreenFooterButton, IHasPopover { [BackgroundDependencyLoader] private void load(OsuColour colour) diff --git a/osu.Game/Screens/Select/FooterV2/FooterButtonRandomV2.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs similarity index 97% rename from osu.Game/Screens/Select/FooterV2/FooterButtonRandomV2.cs rename to osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs index 70d1c0c19e..e8e850a9ce 100644 --- a/osu.Game/Screens/Select/FooterV2/FooterButtonRandomV2.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs @@ -10,12 +10,13 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; +using osu.Game.Screens.Footer; using osuTK; using osuTK.Input; -namespace osu.Game.Screens.Select.FooterV2 +namespace osu.Game.Screens.SelectV2.Footer { - public partial class FooterButtonRandomV2 : FooterButtonV2 + public partial class ScreenFooterButtonRandom : ScreenFooterButton { public Action? NextRandom { get; set; } public Action? PreviousRandom { get; set; } From e3afd89dc879d6b21c41abc2df749729d314fedc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 04:49:33 +0300 Subject: [PATCH 479/581] Allow specifying height of `ShearedButton`s Also includes a test case in `TestSceneShearedButton`s to ensure the buttons' shear factors don't change on different heights (I've encountered issues with that previously). --- .../UserInterface/TestSceneShearedButtons.cs | 51 +++++++++++++++++-- .../Graphics/UserInterface/ShearedButton.cs | 9 ++-- .../Mods/ModFooterInformationDisplay.cs | 2 +- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs index 118d32ee70..8db22f2d65 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedButtons.cs @@ -7,11 +7,13 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -35,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (bigButton) { - Child = button = new ShearedButton(400) + Child = button = new ShearedButton(400, 80) { LighterColour = Colour4.FromHex("#FFFFFF"), DarkerColour = Colour4.FromHex("#FFCC22"), @@ -44,13 +46,12 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Let's GO!", - Height = 80, Action = () => actionFired = true, }; } else { - Child = button = new ShearedButton(200) + Child = button = new ShearedButton(200, 80) { LighterColour = Colour4.FromHex("#FF86DD"), DarkerColour = Colour4.FromHex("#DE31AE"), @@ -58,7 +59,6 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Press me", - Height = 80, Action = () => actionFired = true, }; } @@ -171,5 +171,48 @@ namespace osu.Game.Tests.Visual.UserInterface void setToggleDisabledState(bool disabled) => AddStep($"{(disabled ? "disable" : "enable")} toggle", () => button.Active.Disabled = disabled); } + + [Test] + public void TestButtons() + { + AddStep("create buttons", () => Children = new[] + { + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Scale = new Vector2(2.5f), + Children = new Drawable[] + { + new ShearedButton(120) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "Test", + Action = () => { }, + Padding = new MarginPadding(), + }, + new ShearedButton(120, 40) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "Test", + Action = () => { }, + Padding = new MarginPadding { Left = -1f }, + }, + new ShearedButton(120, 70) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = "Test", + Action = () => { }, + Padding = new MarginPadding { Left = 3f }, + }, + } + } + }); + } } } diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index b1e7066a01..caf1f76d88 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.UserInterface { public partial class ShearedButton : OsuClickableContainer { - public const float HEIGHT = 50; + public const float DEFAULT_HEIGHT = 50; public const float CORNER_RADIUS = 7; public const float BORDER_THICKNESS = 2; @@ -85,10 +85,11 @@ namespace osu.Game.Graphics.UserInterface /// If a value is provided (or the argument is omitted entirely), the button will autosize in width to fit the text. /// /// - public ShearedButton(float? width = null) + /// The height of the button. + public ShearedButton(float? width = null, float height = DEFAULT_HEIGHT) { - Height = HEIGHT; - Padding = new MarginPadding { Horizontal = shear * 50 }; + Height = height; + Padding = new MarginPadding { Horizontal = shear * height }; Content.CornerRadius = CORNER_RADIUS; Content.Shear = new Vector2(shear, 0); diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs index 7fccf0cc13..8668879850 100644 --- a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, - Height = ShearedButton.HEIGHT, + Height = ShearedButton.DEFAULT_HEIGHT, Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), CornerRadius = ShearedButton.CORNER_RADIUS, BorderThickness = ShearedButton.BORDER_THICKNESS, From 266f0803624415acbf6bde6eb3a9b63f7ea9b83c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 05:01:49 +0300 Subject: [PATCH 480/581] Allow customising content of `ShearedButton`s --- .../Graphics/UserInterface/ShearedButton.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index caf1f76d88..0fd21502a1 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -75,6 +75,8 @@ namespace osu.Game.Graphics.UserInterface private readonly Container backgroundLayer; private readonly Box flashLayer; + protected readonly Container ButtonContent; + /// /// Creates a new /// @@ -110,12 +112,16 @@ namespace osu.Game.Graphics.UserInterface { RelativeSizeAxes = Axes.Both }, - text = new OsuSpriteText + ButtonContent = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.TorusAlternate.With(size: 17), - Shear = new Vector2(-shear, 0) + AutoSizeAxes = Axes.Both, + Shear = new Vector2(-shear, 0), + Child = text = new OsuSpriteText + { + Font = OsuFont.TorusAlternate.With(size: 17), + } }, } }, @@ -189,7 +195,7 @@ namespace osu.Game.Graphics.UserInterface { var colourDark = darkerColour ?? ColourProvider.Background3; var colourLight = lighterColour ?? ColourProvider.Background1; - var colourText = textColour ?? ColourProvider.Content1; + var colourContent = textColour ?? ColourProvider.Content1; if (!Enabled.Value) { @@ -206,9 +212,9 @@ namespace osu.Game.Graphics.UserInterface backgroundLayer.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(colourDark, colourLight), 150, Easing.OutQuint); if (!Enabled.Value) - colourText = colourText.Opacity(0.6f); + colourContent = colourContent.Opacity(0.6f); - text.FadeColour(colourText, 150, Easing.OutQuint); + ButtonContent.FadeColour(colourContent, 150, Easing.OutQuint); } } } From 7e8d5a5b0a1d0c58aab8de8afd6dc56094fd4f00 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 04:50:51 +0300 Subject: [PATCH 481/581] Add new footer back button --- osu.Game/Screens/Footer/ScreenBackButton.cs | 64 +++++++++++++++++++++ osu.Game/Screens/Footer/ScreenFooter.cs | 17 ++++-- 2 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Screens/Footer/ScreenBackButton.cs diff --git a/osu.Game/Screens/Footer/ScreenBackButton.cs b/osu.Game/Screens/Footer/ScreenBackButton.cs new file mode 100644 index 0000000000..c5e613ea51 --- /dev/null +++ b/osu.Game/Screens/Footer/ScreenBackButton.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Footer +{ + public partial class ScreenBackButton : ShearedButton + { + // todo: see https://github.com/ppy/osu-framework/issues/3271 + private const float torus_scale_factor = 1.2f; + + public const float BUTTON_WIDTH = 240; + + public ScreenBackButton() + : base(BUTTON_WIDTH, 70) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ButtonContent.Child = new FillFlowContainer + { + X = -10f, + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(20f, 0f), + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20f), + Icon = FontAwesome.Solid.ChevronLeft, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.TorusAlternate.With(size: 20 * torus_scale_factor), + Text = CommonStrings.Back, + UseFullGlyphHeight = false, + } + } + }; + + DarkerColour = Color4Extensions.FromHex("#DE31AE"); + LighterColour = Color4Extensions.FromHex("#FF86DD"); + TextColour = Color4.White; + } + } +} diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index 01013bb466..4a10a4cdab 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; @@ -15,9 +14,8 @@ namespace osu.Game.Screens.Footer { public partial class ScreenFooter : InputBlockingContainer { - //Should be 60, setting to 50 for now for the sake of matching the current BackButton height. - private const int height = 50; - private const int padding = 80; + private const int height = 60; + private const int padding = 60; private readonly List overlays = new List(); @@ -68,13 +66,20 @@ namespace osu.Game.Screens.Footer }, buttons = new FillFlowContainer { - Position = new Vector2(TwoLayerButton.SIZE_EXTENDED.X + padding, 10), + Position = new Vector2(ScreenBackButton.BUTTON_WIDTH + padding, 10), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Direction = FillDirection.Horizontal, Spacing = new Vector2(7, 0), AutoSizeAxes = Axes.Both - } + }, + new ScreenBackButton + { + Margin = new MarginPadding { Bottom = 10f, Left = 12f }, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => { }, + }, }; } } From 9446f45acf7da4bea4f042dc68a36e2c7d42db53 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 05:03:34 +0300 Subject: [PATCH 482/581] Fix shear factor of `ScreenFooterButton`s not matching anything else When shear factors differ between components that are close to each other (`BackButtonV2` and `ScreenFooterButton` in this case), they look completely ugly. --- osu.Game/Screens/Footer/ScreenFooterButton.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index b3b3c9a8ec..3a23249ae0 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -25,8 +25,9 @@ namespace osu.Game.Screens.Footer { public partial class ScreenFooterButton : OsuClickableContainer, IKeyBindingHandler { - // This should be 12 by design, but an extra allowance is added due to the corner radius specification. - private const float shear_width = 13.5f; + // if we go by design, both this and ShearedButton should have shear factor as 1/7, + // but ShearedButton uses 1/5 so we must follow suit for the design to stay consistent. + private const float shear = 1f / 5f; protected const int CORNER_RADIUS = 10; protected const int BUTTON_HEIGHT = 90; @@ -34,7 +35,7 @@ namespace osu.Game.Screens.Footer public Bindable OverlayState = new Bindable(); - protected static readonly Vector2 BUTTON_SHEAR = new Vector2(shear_width / BUTTON_HEIGHT, 0); + protected static readonly Vector2 BUTTON_SHEAR = new Vector2(shear, 0); [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; From 7fce4cc4948803047de66133b07060a9991ff426 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 05:30:19 +0300 Subject: [PATCH 483/581] Make `ScreenFooterButtonMods` share shear factor with base class --- .../SelectV2/Footer/ScreenFooterButtonMods.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs index 49961c60f8..f590a19164 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs @@ -33,12 +33,9 @@ namespace osu.Game.Screens.SelectV2.Footer { // todo: see https://github.com/ppy/osu-framework/issues/3271 private const float torus_scale_factor = 1.2f; - private const float bar_shear_width = 7f; private const float bar_height = 37f; private const float mod_display_portion = 0.65f; - private static readonly Vector2 bar_shear = new Vector2(bar_shear_width / bar_height, 0); - private readonly BindableWithCurrent> current = new BindableWithCurrent>(Array.Empty()); public Bindable> Current @@ -77,7 +74,7 @@ namespace osu.Game.Screens.SelectV2.Footer Y = -5f, Depth = float.MaxValue, Origin = Anchor.BottomLeft, - Shear = bar_shear, + Shear = BUTTON_SHEAR, CornerRadius = CORNER_RADIUS, Size = new Vector2(BUTTON_WIDTH, bar_height), Masking = true, @@ -107,7 +104,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -bar_shear, + Shear = -BUTTON_SHEAR, UseFullGlyphHeight = false, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold) } @@ -129,7 +126,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -bar_shear, + Shear = -BUTTON_SHEAR, Scale = new Vector2(0.6f), Current = { BindTarget = Current }, ExpansionMode = ExpansionMode.AlwaysContracted, @@ -138,7 +135,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -bar_shear, + Shear = -BUTTON_SHEAR, Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold), Mods = { BindTarget = Current }, } @@ -304,7 +301,7 @@ namespace osu.Game.Screens.SelectV2.Footer Y = -5f; Depth = float.MaxValue; Origin = Anchor.BottomLeft; - Shear = bar_shear; + Shear = BUTTON_SHEAR; CornerRadius = CORNER_RADIUS; AutoSizeAxes = Axes.X; Height = bar_height; @@ -328,7 +325,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -bar_shear, + Shear = -BUTTON_SHEAR, Text = ModSelectOverlayStrings.Unranked.ToUpper(), Margin = new MarginPadding { Horizontal = 15 }, UseFullGlyphHeight = false, From 9265290acfea0c3d746ede43426ae479708e340b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 05:30:35 +0300 Subject: [PATCH 484/581] Change shear factor everywhere to 0.15x --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 3 ++- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 3 ++- osu.Game/Screens/Footer/ScreenFooterButton.cs | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index 0fd21502a1..d546c18cb8 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -11,6 +11,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; +using osu.Game.Overlays.Mods; using osuTK; namespace osu.Game.Graphics.UserInterface @@ -66,7 +67,7 @@ namespace osu.Game.Graphics.UserInterface private readonly Box background; private readonly OsuSpriteText text; - private const float shear = 0.2f; + private const float shear = ShearedOverlayContainer.SHEAR; private Colour4? darkerColour; private Colour4? lighterColour; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index a372ec70db..893ac89aa4 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -22,7 +22,8 @@ namespace osu.Game.Overlays.Mods { protected const float PADDING = 14; - public const float SHEAR = 0.2f; + // todo: maybe move this to a higher place since it's used for screen footer buttons etc. + public const float SHEAR = 0.15f; [Cached] protected readonly OverlayColourProvider ColourProvider; diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index 3a23249ae0..e0b019bfa8 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Overlays; +using osu.Game.Overlays.Mods; using osuTK; using osuTK.Graphics; @@ -25,9 +26,7 @@ namespace osu.Game.Screens.Footer { public partial class ScreenFooterButton : OsuClickableContainer, IKeyBindingHandler { - // if we go by design, both this and ShearedButton should have shear factor as 1/7, - // but ShearedButton uses 1/5 so we must follow suit for the design to stay consistent. - private const float shear = 1f / 5f; + private const float shear = ShearedOverlayContainer.SHEAR; protected const int CORNER_RADIUS = 10; protected const int BUTTON_HEIGHT = 90; From 310b4d90cc49fc8f26982bc0656285458d713d6c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 07:27:54 +0300 Subject: [PATCH 485/581] Move `SHEAR` constant to `OsuGame` and revert back to 0.2x (i.e. master) Discussed in [discord](https://discord.com/channels/188630481301012481/188630652340404224/1240490608934653984). --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 2 +- osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs | 4 ++-- osu.Game/OsuGame.cs | 5 +++++ osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 2 +- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs | 2 +- osu.Game/Overlays/Mods/ModPanel.cs | 2 +- osu.Game/Overlays/Mods/ModSelectColumn.cs | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectPanel.cs | 8 ++++---- osu.Game/Overlays/Mods/RankingInformationDisplay.cs | 6 +++--- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 3 --- osu.Game/Screens/Footer/ScreenFooterButton.cs | 2 +- 13 files changed, 25 insertions(+), 23 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index d546c18cb8..7eed388707 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -67,7 +67,7 @@ namespace osu.Game.Graphics.UserInterface private readonly Box background; private readonly OsuSpriteText text; - private const float shear = ShearedOverlayContainer.SHEAR; + private const float shear = OsuGame.SHEAR; private Colour4? darkerColour; private Colour4? lighterColour; diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index c3a9f8a586..5f3716f36c 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -53,7 +53,7 @@ namespace osu.Game.Graphics.UserInterface public ShearedSearchTextBox() { Height = 42; - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + Shear = new Vector2(OsuGame.SHEAR, 0); Masking = true; CornerRadius = corner_radius; @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = CommonStrings.InputSearch; CornerRadius = corner_radius; - TextContainer.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0); + TextContainer.Shear = new Vector2(-OsuGame.SHEAR, 0); } protected override SpriteText CreatePlaceholder() => new SearchPlaceholder(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7c89314014..af01a1b1ac 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -91,6 +91,11 @@ namespace osu.Game /// protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.05f; + /// + /// A common shear factor applied to most components of the game. + /// + public const float SHEAR = 0.2f; + public Toolbar Toolbar { get; private set; } private ChatOverlay chatOverlay; diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index c58cf710bd..5b10a2844e 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { - const float shear = ShearedOverlayContainer.SHEAR; + const float shear = OsuGame.SHEAR; LeftContent.AddRange(new Drawable[] { diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index e9f21338bd..326394a207 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -106,7 +106,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Scale = new Vector2(0.8f), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) + Shear = new Vector2(-OsuGame.SHEAR, 0) }); ItemsFlow.Padding = new MarginPadding { diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs index 8668879850..6665a3b8dc 100644 --- a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, Height = ShearedButton.DEFAULT_HEIGHT, - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(OsuGame.SHEAR, 0), CornerRadius = ShearedButton.CORNER_RADIUS, BorderThickness = ShearedButton.BORDER_THICKNESS, Masking = true, diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index cf173b0d6a..9f87a704c0 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Active = { BindTarget = Active }, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) }; } diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 61b29ef65b..5ffed24e7a 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Mods { Width = WIDTH; RelativeSizeAxes = Axes.Y; - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + Shear = new Vector2(OsuGame.SHEAR, 0); InternalChildren = new Drawable[] { @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.X, Height = header_height, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Velocity = 0.7f, ClampAxes = Axes.Y }, @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Padding = new MarginPadding { Horizontal = 17, diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 25293e8e20..f521b2e38c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Direction = FillDirection.Horizontal, - Shear = new Vector2(SHEAR, 0), + Shear = new Vector2(OsuGame.SHEAR, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Margin = new MarginPadding { Horizontal = 70 }, @@ -847,7 +847,7 @@ namespace osu.Game.Overlays.Mods // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent); - var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent); + var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * OsuGame.SHEAR, 0), ScrollContent); bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) && Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X); diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 29f4c93e88..284356f37e 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Mods Content.CornerRadius = CORNER_RADIUS; Content.BorderThickness = 2; - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + Shear = new Vector2(OsuGame.SHEAR, 0); Children = new Drawable[] { @@ -128,10 +128,10 @@ namespace osu.Game.Overlays.Mods { Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Margin = new MarginPadding { - Left = -18 * ShearedOverlayContainer.SHEAR + Left = -18 * OsuGame.SHEAR }, ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. }, @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.Mods { Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. } } diff --git a/osu.Game/Overlays/Mods/RankingInformationDisplay.cs b/osu.Game/Overlays/Mods/RankingInformationDisplay.cs index 494f8a377f..75a8f289d8 100644 --- a/osu.Game/Overlays/Mods/RankingInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/RankingInformationDisplay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, RelativeSizeAxes = Axes.Both, - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(OsuGame.SHEAR, 0), CornerRadius = ShearedButton.CORNER_RADIUS, Masking = true, Children = new Drawable[] @@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } } @@ -94,7 +94,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Child = counter = new EffectCounter { - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Shear = new Vector2(-OsuGame.SHEAR, 0), Anchor = Anchor.Centre, Origin = Anchor.Centre, Current = { BindTarget = ModMultiplier } diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 893ac89aa4..acdd1db728 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -22,9 +22,6 @@ namespace osu.Game.Overlays.Mods { protected const float PADDING = 14; - // todo: maybe move this to a higher place since it's used for screen footer buttons etc. - public const float SHEAR = 0.15f; - [Cached] protected readonly OverlayColourProvider ColourProvider; diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index e0b019bfa8..40e79ed474 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Footer { public partial class ScreenFooterButton : OsuClickableContainer, IKeyBindingHandler { - private const float shear = ShearedOverlayContainer.SHEAR; + private const float shear = OsuGame.SHEAR; protected const int CORNER_RADIUS = 10; protected const int BUTTON_HEIGHT = 90; From 820cfbcb005f759d1e0f1858781d762028ff08e2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 07:47:30 +0300 Subject: [PATCH 486/581] Remove unused using directives --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 1 - osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs | 1 - osu.Game/Screens/Footer/ScreenFooterButton.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index 7eed388707..87d269ccd4 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -11,7 +11,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osu.Game.Overlays.Mods; using osuTK; namespace osu.Game.Graphics.UserInterface diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 5f3716f36c..c6565726b5 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osu.Game.Overlays.Mods; using osu.Game.Resources.Localisation.Web; using osuTK; diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index 40e79ed474..8fbd9f6cba 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -18,7 +18,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Overlays; -using osu.Game.Overlays.Mods; using osuTK; using osuTK.Graphics; From 97de73b99c47b2c0787ece0efa8431272df49c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 16 May 2024 08:21:52 +0200 Subject: [PATCH 487/581] Do not change mania column width on mobile platforms - Closes https://github.com/ppy/osu/issues/25852 - Reverts https://github.com/ppy/osu/pull/25336 / https://github.com/ppy/osu/pull/25777 With the columns not being directly touchable anymore after https://github.com/ppy/osu/pull/28173 I see very little point to this continuing to exist. --- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 36 ------------------------ 1 file changed, 36 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 1593e8e76f..f444448797 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -3,15 +3,12 @@ #nullable disable -using System; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Rulesets.Mania.UI { @@ -62,12 +59,6 @@ namespace osu.Game.Rulesets.Mania.UI onSkinChanged(); } - protected override void LoadComplete() - { - base.LoadComplete(); - updateMobileSizing(); - } - private void onSkinChanged() { for (int i = 0; i < stageDefinition.Columns; i++) @@ -92,8 +83,6 @@ namespace osu.Game.Rulesets.Mania.UI columns[i].Width = width.Value; } - - updateMobileSizing(); } /// @@ -106,31 +95,6 @@ namespace osu.Game.Rulesets.Mania.UI Content[column] = columns[column].Child = content; } - private void updateMobileSizing() - { - if (!IsLoaded || !RuntimeInfo.IsMobile) - return; - - // GridContainer+CellContainer containing this stage (gets split up for dual stages). - Vector2? containingCell = this.FindClosestParent()?.Parent?.DrawSize; - - // Will be null in tests. - if (containingCell == null) - return; - - float aspectRatio = containingCell.Value.X / containingCell.Value.Y; - - // 2.83 is a mostly arbitrary scale-up (170 / 60, based on original implementation for argon) - float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns); - // 1.92 is a "reference" mobile screen aspect ratio for phones. - // We should scale it back for cases like tablets which aren't so extreme. - mobileAdjust *= aspectRatio / 1.92f; - - // Best effort until we have better mobile support. - for (int i = 0; i < stageDefinition.Columns; i++) - columns[i].Width *= mobileAdjust; - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 5dd64a7c86bae98bc991386ba92faff97d6a205d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 16 May 2024 14:49:56 +0200 Subject: [PATCH 488/581] Fix duplicated localisation key in `DeleteConfiormationContentStrings` Noticed via `osu-resources` build warnings. There are also a few other warnings about https://github.com/ppy/osu/pull/27472. Seems something in crowdin innards may still be exporting those strings even though they have been fixed already. Not sure how to address that. Probably need these to be detected via static analysis at this point since it's happened again. Might look into the feasibility of making that happen. --- osu.Game/Localisation/DeleteConfirmationContentStrings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs index 563fbf5654..d781fadbce 100644 --- a/osu.Game/Localisation/DeleteConfirmationContentStrings.cs +++ b/osu.Game/Localisation/DeleteConfirmationContentStrings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Localisation /// /// "Are you sure you want to delete all scores? This cannot be undone!" /// - public static LocalisableString Scores => new TranslatableString(getKey(@"collections"), @"Are you sure you want to delete all scores? This cannot be undone!"); + public static LocalisableString Scores => new TranslatableString(getKey(@"scores"), @"Are you sure you want to delete all scores? This cannot be undone!"); /// /// "Are you sure you want to delete all mod presets?" From f2f03f08cb0dd79de4587a2ec63da97f3cb375da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 16 May 2024 17:36:19 +0200 Subject: [PATCH 489/581] Fix xmldoc mismatches in localisation files This is enforced by the localisation analyser after https://github.com/ppy/osu-localisation-analyser/pull/62, but it appears the analyser was never actually bumped game-side after that change and I'm not super sure why, as there does not appear to be a reason to _not_ do that. So this commit does it. --- .../FirstRunOverlayImportFromStableScreenStrings.cs | 2 +- osu.Game/Localisation/NotificationsStrings.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs index 04fecab3df..521a77fe20 100644 --- a/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs +++ b/osu.Game/Localisation/FirstRunOverlayImportFromStableScreenStrings.cs @@ -47,7 +47,7 @@ namespace osu.Game.Localisation public static LocalisableString Calculating => new TranslatableString(getKey(@"calculating"), @"calculating..."); /// - /// "{0} items" + /// "{0} item(s)" /// public static LocalisableString Items(int arg0) => new TranslatableString(getKey(@"items"), @"{0} item(s)", arg0); diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs index 3188ca5533..698fe230b2 100644 --- a/osu.Game/Localisation/NotificationsStrings.cs +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -114,7 +114,7 @@ Please try changing your audio device to a working setting."); public static LocalisableString MismatchingBeatmapForReplay => new TranslatableString(getKey(@"mismatching_beatmap_for_replay"), @"Your local copy of the beatmap for this replay appears to be different than expected. You may need to update or re-download it."); /// - /// "You are now running osu! {version}. + /// "You are now running osu! {0}. /// Click to see what's new!" /// public static LocalisableString GameVersionAfterUpdate(string version) => new TranslatableString(getKey(@"game_version_after_update"), @"You are now running osu! {0}. diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5452a648d1..7588b2b3c8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From a3dfd99f7d5895fb0bf5f0c8398245a42b6d2848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 16 May 2024 18:23:19 +0200 Subject: [PATCH 490/581] Fix discord arbitrarily refusing to work on "too short" strings Closes https://github.com/ppy/osu/issues/28192. --- osu.Desktop/DiscordRichPresence.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 74ebd38f2c..3e0a9099cb 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -164,8 +164,8 @@ namespace osu.Desktop // user activity if (activity.Value != null) { - presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation)); - presence.Details = truncate(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty); + presence.State = clampLength(activity.Value.GetStatus(hideIdentifiableInformation)); + presence.Details = clampLength(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty); if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0) { @@ -271,8 +271,15 @@ namespace osu.Desktop private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' }); - private static string truncate(string str) + private static string clampLength(string str) { + // For whatever reason, discord decides that strings shorter than 2 characters cannot possibly be valid input, because... reasons? + // And yes, that is two *characters*, or *codepoints*, not *bytes* as further down below (as determined by empirical testing). + // That seems very questionable, and isn't even documented anywhere. So to *make it* accept such valid input, + // just tack on enough of U+200B ZERO WIDTH SPACEs at the end. + if (str.Length < 2) + return str.PadRight(2, '\u200B'); + if (Encoding.UTF8.GetByteCount(str) <= 128) return str; From 2f9d74286dd5401450f071659323415c3329d3b7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 17 May 2024 11:15:17 +0900 Subject: [PATCH 491/581] Bump once more --- .config/dotnet-tools.json | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 99906f0895..ace7db82f8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -21,7 +21,7 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2023.1117.0", + "version": "2024.517.0", "commands": [ "localisation" ] diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7588b2b3c8..f91995feff 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 61a415fed2ae74c50223015d957bcc55b8a11c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 May 2024 10:32:39 +0200 Subject: [PATCH 492/581] Add client/server models & operations for "daily challenge" feature --- osu.Game/Online/Metadata/DailyChallengeInfo.cs | 16 ++++++++++++++++ osu.Game/Online/Metadata/IMetadataClient.cs | 6 ++++++ osu.Game/Online/Metadata/IMetadataServer.cs | 7 ++++++- osu.Game/Online/Metadata/MetadataClient.cs | 9 +++++++++ osu.Game/Online/Metadata/OnlineMetadataClient.cs | 11 +++++++++++ osu.Game/Online/Rooms/RoomCategory.cs | 3 +++ .../Tests/Visual/Metadata/TestMetadataClient.cs | 9 +++++++++ 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/Metadata/DailyChallengeInfo.cs diff --git a/osu.Game/Online/Metadata/DailyChallengeInfo.cs b/osu.Game/Online/Metadata/DailyChallengeInfo.cs new file mode 100644 index 0000000000..7c49556653 --- /dev/null +++ b/osu.Game/Online/Metadata/DailyChallengeInfo.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using MessagePack; + +namespace osu.Game.Online.Metadata +{ + [MessagePackObject] + [Serializable] + public struct DailyChallengeInfo + { + [Key(0)] + public long RoomID { get; set; } + } +} diff --git a/osu.Game/Online/Metadata/IMetadataClient.cs b/osu.Game/Online/Metadata/IMetadataClient.cs index 7102554ae9..ee7a726bfc 100644 --- a/osu.Game/Online/Metadata/IMetadataClient.cs +++ b/osu.Game/Online/Metadata/IMetadataClient.cs @@ -20,5 +20,11 @@ namespace osu.Game.Online.Metadata /// Delivers an update of the of the user with the supplied . /// Task UserPresenceUpdated(int userId, UserPresence? status); + + /// + /// Delivers an update of the current "daily challenge" status. + /// Null value means there is no "daily challenge" currently active. + /// + Task DailyChallengeUpdated(DailyChallengeInfo? info); } } diff --git a/osu.Game/Online/Metadata/IMetadataServer.cs b/osu.Game/Online/Metadata/IMetadataServer.cs index 9780045333..8bf3f8f56b 100644 --- a/osu.Game/Online/Metadata/IMetadataServer.cs +++ b/osu.Game/Online/Metadata/IMetadataServer.cs @@ -7,7 +7,12 @@ using osu.Game.Users; namespace osu.Game.Online.Metadata { /// - /// Metadata server is responsible for keeping the osu! client up-to-date with any changes. + /// Metadata server is responsible for keeping the osu! client up-to-date with various real-time happenings, such as: + /// + /// beatmap updates via BSS, + /// online user activity/status updates, + /// other real-time happenings, such as current "daily challenge" status. + /// /// public interface IMetadataServer { diff --git a/osu.Game/Online/Metadata/MetadataClient.cs b/osu.Game/Online/Metadata/MetadataClient.cs index 8e99a9b2cb..b619970494 100644 --- a/osu.Game/Online/Metadata/MetadataClient.cs +++ b/osu.Game/Online/Metadata/MetadataClient.cs @@ -59,6 +59,15 @@ namespace osu.Game.Online.Metadata #endregion + #region Daily Challenge + + public abstract IBindable DailyChallengeInfo { get; } + + /// + public abstract Task DailyChallengeUpdated(DailyChallengeInfo? info); + + #endregion + #region Disconnection handling public event Action? Disconnecting; diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 3805d12688..b94f26a71d 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -26,6 +26,9 @@ namespace osu.Game.Online.Metadata public override IBindableDictionary UserStates => userStates; private readonly BindableDictionary userStates = new BindableDictionary(); + public override IBindable DailyChallengeInfo => dailyChallengeInfo; + private readonly Bindable dailyChallengeInfo = new Bindable(); + private readonly string endpoint; private IHubClientConnector? connector; @@ -58,6 +61,7 @@ namespace osu.Game.Online.Metadata // https://github.com/dotnet/aspnetcore/issues/15198 connection.On(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated); connection.On(nameof(IMetadataClient.UserPresenceUpdated), ((IMetadataClient)this).UserPresenceUpdated); + connection.On(nameof(IMetadataClient.DailyChallengeUpdated), ((IMetadataClient)this).DailyChallengeUpdated); connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMetadataClient)this).DisconnectRequested); }; @@ -101,6 +105,7 @@ namespace osu.Game.Online.Metadata { isWatchingUserPresence.Value = false; userStates.Clear(); + dailyChallengeInfo.Value = null; }); return; } @@ -229,6 +234,12 @@ namespace osu.Game.Online.Metadata } } + public override Task DailyChallengeUpdated(DailyChallengeInfo? info) + { + Schedule(() => dailyChallengeInfo.Value = info); + return Task.CompletedTask; + } + public override async Task DisconnectRequested() { await base.DisconnectRequested().ConfigureAwait(false); diff --git a/osu.Game/Online/Rooms/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs index 17afb0dc7f..4534e7de59 100644 --- a/osu.Game/Online/Rooms/RoomCategory.cs +++ b/osu.Game/Online/Rooms/RoomCategory.cs @@ -13,5 +13,8 @@ namespace osu.Game.Online.Rooms [Description("Featured Artist")] FeaturedArtist, + + [Description("Daily Challenge")] + DailyChallenge, } } diff --git a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs index 16cbf879df..b589e66d8b 100644 --- a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs +++ b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs @@ -21,6 +21,9 @@ namespace osu.Game.Tests.Visual.Metadata public override IBindableDictionary UserStates => userStates; private readonly BindableDictionary userStates = new BindableDictionary(); + public override IBindable DailyChallengeInfo => dailyChallengeInfo; + private readonly Bindable dailyChallengeInfo = new Bindable(); + [Resolved] private IAPIProvider api { get; set; } = null!; @@ -77,5 +80,11 @@ namespace osu.Game.Tests.Visual.Metadata => Task.FromResult(new BeatmapUpdates(Array.Empty(), queueId)); public override Task BeatmapSetsUpdated(BeatmapUpdates updates) => Task.CompletedTask; + + public override Task DailyChallengeUpdated(DailyChallengeInfo? info) + { + dailyChallengeInfo.Value = info; + return Task.CompletedTask; + } } } From a4f8ed2a0ef48d8acf77877606b9d733e4170e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 May 2024 10:39:24 +0200 Subject: [PATCH 493/581] Add button to access daily challenge playlist from main menu --- .../UserInterface/TestSceneMainMenuButton.cs | 83 +++++++ .../UpdateableOnlineBeatmapSetCover.cs | 6 +- osu.Game/Graphics/OsuIcon.cs | 4 + osu.Game/Localisation/ButtonSystemStrings.cs | 7 +- osu.Game/Screens/Menu/ButtonSystem.cs | 57 +++-- osu.Game/Screens/Menu/DailyChallengeButton.cs | 209 ++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 6 + osu.Game/Screens/Menu/MainMenuButton.cs | 183 +++++++++------ .../Screens/OnlinePlay/Playlists/Playlists.cs | 3 + 9 files changed, 476 insertions(+), 82 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs create mode 100644 osu.Game/Screens/Menu/DailyChallengeButton.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs new file mode 100644 index 0000000000..921e28d607 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs @@ -0,0 +1,83 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Localisation; +using osu.Game.Online.API; +using osu.Game.Online.Metadata; +using osu.Game.Online.Rooms; +using osu.Game.Screens.Menu; +using osuTK.Input; +using Color4 = osuTK.Graphics.Color4; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public partial class TestSceneMainMenuButton : OsuTestScene + { + [Resolved] + private MetadataClient metadataClient { get; set; } = null!; + + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; + + [Test] + public void TestStandardButton() + { + AddStep("add button", () => Child = new MainMenuButton( + ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => { }, 0, Key.P) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ButtonSystemState = ButtonSystemState.TopLevel, + }); + } + + [Test] + public void TestDailyChallengeButton() + { + AddStep("add button", () => Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ButtonSystemState = ButtonSystemState.TopLevel, + }); + + AddStep("set up API", () => dummyAPI.HandleRequest = req => + { + switch (req) + { + case GetRoomRequest getRoomRequest: + if (getRoomRequest.RoomId != 1234) + return false; + + var beatmap = CreateAPIBeatmap(); + beatmap.OnlineID = 1001; + getRoomRequest.TriggerSuccess(new Room + { + RoomID = { Value = 1234 }, + Playlist = + { + new PlaylistItem(beatmap) + }, + EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) } + }); + return true; + + default: + return false; + } + }); + + AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo + { + RoomID = 1234, + })); + + AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null)); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs index 93b0dd5c15..2a6b6f90e3 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs @@ -43,7 +43,11 @@ namespace osu.Game.Beatmaps.Drawables protected override double TransformDuration => 400; protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) - => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad); + => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; protected override Drawable CreateDrawable(IBeatmapSetOnlineInfo model) { diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index 32e780f11c..9879ef5d14 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -120,6 +120,7 @@ namespace osu.Game.Graphics public static IconUsage Cross => get(OsuIconMapping.Cross); public static IconUsage CrossCircle => get(OsuIconMapping.CrossCircle); public static IconUsage Crown => get(OsuIconMapping.Crown); + public static IconUsage DailyChallenge => get(OsuIconMapping.DailyChallenge); public static IconUsage Debug => get(OsuIconMapping.Debug); public static IconUsage Delete => get(OsuIconMapping.Delete); public static IconUsage Details => get(OsuIconMapping.Details); @@ -218,6 +219,9 @@ namespace osu.Game.Graphics [Description(@"crown")] Crown, + [Description(@"daily-challenge")] + DailyChallenge, + [Description(@"debug")] Debug, diff --git a/osu.Game/Localisation/ButtonSystemStrings.cs b/osu.Game/Localisation/ButtonSystemStrings.cs index ba4abf63a6..b0a205eebe 100644 --- a/osu.Game/Localisation/ButtonSystemStrings.cs +++ b/osu.Game/Localisation/ButtonSystemStrings.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Localisation; @@ -54,6 +54,11 @@ namespace osu.Game.Localisation /// public static LocalisableString Exit => new TranslatableString(getKey(@"exit"), @"exit"); + /// + /// "daily challenge" + /// + public static LocalisableString DailyChallenge => new TranslatableString(getKey(@"daily_challenge"), @"daily challenge"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 15a2740160..33d2a8d348 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -24,6 +24,7 @@ using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Online.API; +using osu.Game.Online.Rooms; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -46,6 +47,7 @@ namespace osu.Game.Screens.Menu public Action? OnSettings; public Action? OnMultiplayer; public Action? OnPlaylists; + public Action? OnDailyChallenge; private readonly IBindable isIdle = new BindableBool(); @@ -102,10 +104,13 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { - new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O, Key.S), - backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, - -WEDGE_WIDTH) + new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), _ => OnSettings?.Invoke(), Key.O, Key.S) { + Padding = new MarginPadding { Right = WEDGE_WIDTH }, + }, + backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), _ => State = ButtonSystemState.TopLevel) + { + Padding = new MarginPadding { Right = WEDGE_WIDTH }, VisibleStateMin = ButtonSystemState.Play, VisibleStateMax = ButtonSystemState.Edit, }, @@ -127,21 +132,31 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host) { - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), _ => OnSolo?.Invoke(), Key.P) + { + Padding = new MarginPadding { Left = WEDGE_WIDTH }, + }); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, Key.M)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, Key.L)); + buttonsPlay.Add(new DailyChallengeButton(@"button-default-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); - buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B, Key.E)); - buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); + buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E) + { + Padding = new MarginPadding { Left = WEDGE_WIDTH}, + }); + buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), _ => OnEditSkin?.Invoke(), Key.S)); buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P, Key.M, Key.L)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), _ => State = ButtonSystemState.Play, Key.P, Key.M, Key.L) + { + Padding = new MarginPadding { Left = WEDGE_WIDTH }, + }); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), _ => State = ButtonSystemState.Edit, Key.E)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), _ => OnBeatmapListing?.Invoke(), Key.B, Key.D)); if (host.CanExit) - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), _ => OnExit?.Invoke(), Key.Q)); buttonArea.AddRange(buttonsPlay); buttonArea.AddRange(buttonsEdit); @@ -164,7 +179,7 @@ namespace osu.Game.Screens.Menu sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh"); } - private void onMultiplayer() + private void onMultiplayer(MainMenuButton _) { if (api.State.Value != APIState.Online) { @@ -175,7 +190,7 @@ namespace osu.Game.Screens.Menu OnMultiplayer?.Invoke(); } - private void onPlaylists() + private void onPlaylists(MainMenuButton _) { if (api.State.Value != APIState.Online) { @@ -186,6 +201,20 @@ namespace osu.Game.Screens.Menu OnPlaylists?.Invoke(); } + private void onDailyChallenge(MainMenuButton button) + { + if (api.State.Value != APIState.Online) + { + loginOverlay?.Show(); + return; + } + + var dailyChallengeButton = (DailyChallengeButton)button; + + if (dailyChallengeButton.Room != null) + OnDailyChallenge?.Invoke(dailyChallengeButton.Room); + } + private void updateIdleState(bool isIdle) { if (!ReturnToTopOnIdle) diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs new file mode 100644 index 0000000000..907fd04148 --- /dev/null +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -0,0 +1,209 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Threading; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Localisation; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Metadata; +using osu.Game.Online.Rooms; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Screens.Menu +{ + public partial class DailyChallengeButton : MainMenuButton, IHasCustomTooltip + { + public Room? Room { get; private set; } + + private readonly OsuSpriteText countdown; + private ScheduledDelegate? scheduledCountdownUpdate; + + private UpdateableOnlineBeatmapSetCover cover = null!; + private IBindable info = null!; + private BufferedContainer background = null!; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + public DailyChallengeButton(string sampleName, Color4 colour, Action? clickAction = null, params Key[] triggerKeys) + : base(ButtonSystemStrings.DailyChallenge, sampleName, OsuIcon.DailyChallenge, colour, clickAction, triggerKeys) + { + BaseSize = new Vector2(ButtonSystem.BUTTON_WIDTH * 1.3f, ButtonArea.BUTTON_AREA_HEIGHT); + + Content.Add(countdown = new OsuSpriteText + { + Shadow = true, + AllowMultiline = false, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding + { + Left = -3, + Bottom = 22, + }, + Font = OsuFont.Default.With(size: 12), + Alpha = 0, + }); + } + + protected override Drawable CreateBackground(Colour4 accentColour) => background = new BufferedContainer + { + Children = new Drawable[] + { + cover = new UpdateableOnlineBeatmapSetCover + { + RelativeSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + X = -0.5f, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourInfo.GradientVertical(accentColour.Opacity(0), accentColour), + Blending = BlendingParameters.Additive, + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = accentColour.Opacity(0.7f) + }, + }, + }; + + [BackgroundDependencyLoader] + private void load(MetadataClient metadataClient) + { + info = metadataClient.DailyChallengeInfo.GetBoundCopy(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + info.BindValueChanged(updateDisplay, true); + FinishTransforms(true); + + cover.MoveToX(-0.5f, 10000, Easing.InOutSine) + .Then().MoveToX(0.5f, 10000, Easing.InOutSine) + .Loop(); + } + + protected override void Update() + { + base.Update(); + + cover.Width = 2 * background.DrawWidth; + } + + private void updateDisplay(ValueChangedEvent info) + { + UpdateState(); + + scheduledCountdownUpdate?.Cancel(); + scheduledCountdownUpdate = null; + + if (info.NewValue == null) + { + Room = null; + cover.OnlineInfo = TooltipContent = null; + } + else + { + var roomRequest = new GetRoomRequest(info.NewValue.Value.RoomID); + + roomRequest.Success += room => + { + Room = room; + cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet; + + updateCountdown(); + Scheduler.AddDelayed(updateCountdown, 1000, true); + }; + api.Queue(roomRequest); + } + } + + private void updateCountdown() + { + if (Room == null) + return; + + var remaining = (Room.EndDate.Value - DateTimeOffset.Now) ?? TimeSpan.Zero; + + if (remaining <= TimeSpan.Zero) + { + countdown.FadeOut(250, Easing.OutQuint); + } + else + { + if (countdown.Alpha == 0) + countdown.FadeIn(250, Easing.OutQuint); + + countdown.Text = remaining.ToString(@"hh\:mm\:ss"); + } + } + + protected override void UpdateState() + { + if (info.IsNotNull() && info.Value == null) + { + ContractStyle = 0; + State = ButtonState.Contracted; + return; + } + + base.UpdateState(); + } + + public ITooltip GetCustomTooltip() => new DailyChallengeTooltip(); + + public APIBeatmapSet? TooltipContent { get; private set; } + + internal partial class DailyChallengeTooltip : CompositeDrawable, ITooltip + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private APIBeatmapSet? lastContent; + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + } + + public void Move(Vector2 pos) => Position = pos; + + public void SetContent(APIBeatmapSet? content) + { + if (content == lastContent) + return; + + lastContent = content; + + ClearInternal(); + if (content != null) + AddInternal(new BeatmapCardNano(content)); + } + } + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 235c5d5c56..722e776e3d 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -147,6 +147,12 @@ namespace osu.Game.Screens.Menu OnSolo = loadSoloSongSelect, OnMultiplayer = () => this.Push(new Multiplayer()), OnPlaylists = () => this.Push(new Playlists()), + OnDailyChallenge = room => + { + Playlists playlistsScreen; + this.Push(playlistsScreen = new Playlists()); + playlistsScreen.Join(room); + }, OnExit = () => { exitConfirmedViaHoldOrClick = true; diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 1dc79e9b1a..fe8fb91766 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -38,11 +38,8 @@ namespace osu.Game.Screens.Menu public readonly Key[] TriggerKeys; - private readonly Container iconText; - private readonly Container box; - private readonly Box boxHoverLayer; - private readonly SpriteIcon icon; - private readonly string sampleName; + protected override Container Content => content; + private readonly Container content; /// /// The menu state for which we are visible for (assuming only one). @@ -59,7 +56,24 @@ namespace osu.Game.Screens.Menu public ButtonSystemState VisibleStateMin = ButtonSystemState.TopLevel; public ButtonSystemState VisibleStateMax = ButtonSystemState.TopLevel; - private readonly Action? clickAction; + public new MarginPadding Padding + { + get => Content.Padding; + set => Content.Padding = value; + } + + protected Vector2 BaseSize { get; init; } = new Vector2(ButtonSystem.BUTTON_WIDTH, ButtonArea.BUTTON_AREA_HEIGHT); + + private readonly Action? clickAction; + + private readonly Container background; + private readonly Drawable backgroundContent; + private readonly Box boxHoverLayer; + private readonly SpriteIcon icon; + + private Vector2 initialSize => BaseSize + Padding.Total; + + private readonly string sampleName; private Sample? sampleClick; private Sample? sampleHover; private SampleChannel? sampleChannel; @@ -68,9 +82,9 @@ namespace osu.Game.Screens.Menu // Allow keyboard interaction based on state rather than waiting for delayed animations. || state == ButtonState.Expanded; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => background.ReceivePositionalInputAt(screenSpacePos); - public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action? clickAction = null, float extraWidth = 0, params Key[] triggerKeys) + public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action? clickAction = null, params Key[] triggerKeys) { this.sampleName = sampleName; this.clickAction = clickAction; @@ -79,11 +93,9 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both; Alpha = 0; - Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonArea.BUTTON_AREA_HEIGHT); - - Children = new Drawable[] + AddRangeInternal(new Drawable[] { - box = new Container + background = new Container { // box needs to be always present to ensure the button is always sized correctly for flow AlwaysPresent = true, @@ -98,35 +110,46 @@ namespace osu.Game.Screens.Menu }, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Scale = new Vector2(0, 1), - Size = boxSize, - Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / boxSize.Y, 0), Children = new[] { - new Box + backgroundContent = CreateBackground(colour).With(bg => { - EdgeSmoothness = new Vector2(1.5f, 0), - RelativeSizeAxes = Axes.Both, - Colour = colour, - }, + bg.RelativeSizeAxes = Axes.Y; + bg.X = -ButtonSystem.WEDGE_WIDTH; + bg.Anchor = Anchor.Centre; + bg.Origin = Anchor.Centre; + }), boxHoverLayer = new Box { EdgeSmoothness = new Vector2(1.5f, 0), RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Colour = Color4.White, + Depth = float.MinValue, Alpha = 0, }, } }, - iconText = new Container + content = new Container { - AutoSizeAxes = Axes.Both, - Position = new Vector2(extraWidth / 2, 0), + RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] { + new OsuSpriteText + { + Shadow = true, + AllowMultiline = false, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding + { + Left = -3, + Bottom = 7, + }, + Text = text + }, icon = new SpriteIcon { Shadow = true, @@ -136,20 +159,36 @@ namespace osu.Game.Screens.Menu Position = new Vector2(0, 0), Margin = new MarginPadding { Top = -4 }, Icon = symbol - }, - new OsuSpriteText - { - Shadow = true, - AllowMultiline = false, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Position = new Vector2(0, 35), - Margin = new MarginPadding { Left = -3 }, - Text = text } } } - }; + }); + } + + protected virtual Drawable CreateBackground(Colour4 accentColour) => new Container + { + Child = new Box + { + EdgeSmoothness = new Vector2(1.5f, 0), + RelativeSizeAxes = Axes.Both, + Colour = accentColour, + } + }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + background.Size = initialSize; + background.Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / initialSize.Y, 0); + + // for whatever reason, attempting to size the background "just in time" to cover the visible width + // results in gaps when the width changes are quick (only visible when testing menu at 100% speed, not visible slowed down). + // to ensure there's no missing backdrop, just use a ballpark that should be enough to always cover the width and then some. + // note that while on a code inspections it would seem that `1.5 * initialSize.X` would be enough, elastic usings are used in this button + // (which can exceed the [0;1] range during interpolation). + backgroundContent.Width = 2 * initialSize.X; + backgroundContent.Shear = -background.Shear; } private bool rightward; @@ -179,15 +218,15 @@ namespace osu.Game.Screens.Menu { if (State != ButtonState.Expanded) return true; - sampleHover?.Play(); - - box.ScaleTo(new Vector2(1.5f, 1), 500, Easing.OutElastic); - double duration = TimeUntilNextBeat; icon.ClearTransforms(); icon.RotateTo(rightward ? -BOUNCE_ROTATION : BOUNCE_ROTATION, duration, Easing.InOutSine); icon.ScaleTo(new Vector2(HOVER_SCALE, HOVER_SCALE * BOUNCE_COMPRESSION), duration, Easing.Out); + + sampleHover?.Play(); + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(1.5f, 1)), 500, Easing.OutElastic); + return true; } @@ -199,7 +238,7 @@ namespace osu.Game.Screens.Menu icon.ScaleTo(Vector2.One, 200, Easing.Out); if (State == ButtonState.Expanded) - box.ScaleTo(new Vector2(1, 1), 500, Easing.OutElastic); + background.ResizeTo(initialSize, 500, Easing.OutElastic); } [BackgroundDependencyLoader] @@ -246,7 +285,7 @@ namespace osu.Game.Screens.Menu sampleChannel = sampleClick?.GetChannel(); sampleChannel?.Play(); - clickAction?.Invoke(); + clickAction?.Invoke(this); boxHoverLayer.ClearTransforms(); boxHoverLayer.Alpha = 0.9f; @@ -254,13 +293,13 @@ namespace osu.Game.Screens.Menu } public override bool HandleNonPositionalInput => state == ButtonState.Expanded; - public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + public override bool HandlePositionalInput => state != ButtonState.Exploded && background.Width / initialSize.X >= 0.8f; public void StopSamplePlayback() => sampleChannel?.Stop(); protected override void Update() { - iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1); + content.Alpha = Math.Clamp((background.Width / initialSize.X - 0.5f) / 0.3f, 0, 1); base.Update(); } @@ -285,12 +324,12 @@ namespace osu.Game.Screens.Menu switch (ContractStyle) { default: - box.ScaleTo(new Vector2(0, 1), 500, Easing.OutExpo); + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 500, Easing.OutExpo); this.FadeOut(500); break; case 1: - box.ScaleTo(new Vector2(0, 1), 400, Easing.InSine); + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 400, Easing.InSine); this.FadeOut(800); break; } @@ -299,13 +338,13 @@ namespace osu.Game.Screens.Menu case ButtonState.Expanded: const int expand_duration = 500; - box.ScaleTo(new Vector2(1, 1), expand_duration, Easing.OutExpo); + background.ResizeTo(initialSize, expand_duration, Easing.OutExpo); this.FadeIn(expand_duration / 6f); break; case ButtonState.Exploded: const int explode_duration = 200; - box.ScaleTo(new Vector2(2, 1), explode_duration, Easing.OutExpo); + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(2, 1)), explode_duration, Easing.OutExpo); this.FadeOut(explode_duration / 4f * 3); break; } @@ -314,32 +353,44 @@ namespace osu.Game.Screens.Menu } } + private ButtonSystemState buttonSystemState; + public ButtonSystemState ButtonSystemState { + get => buttonSystemState; set { - ContractStyle = 0; + if (buttonSystemState == value) + return; - switch (value) - { - case ButtonSystemState.Initial: + buttonSystemState = value; + UpdateState(); + } + } + + protected virtual void UpdateState() + { + ContractStyle = 0; + + switch (ButtonSystemState) + { + case ButtonSystemState.Initial: + State = ButtonState.Contracted; + break; + + case ButtonSystemState.EnteringMode: + ContractStyle = 1; + State = ButtonState.Contracted; + break; + + default: + if (ButtonSystemState <= VisibleStateMax && ButtonSystemState >= VisibleStateMin) + State = ButtonState.Expanded; + else if (ButtonSystemState < VisibleStateMin) State = ButtonState.Contracted; - break; - - case ButtonSystemState.EnteringMode: - ContractStyle = 1; - State = ButtonState.Contracted; - break; - - default: - if (value <= VisibleStateMax && value >= VisibleStateMin) - State = ButtonState.Expanded; - else if (value < VisibleStateMin) - State = ButtonState.Contracted; - else - State = ButtonState.Exploded; - break; - } + else + State = ButtonState.Exploded; + break; } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs index f1d2384c2f..9e615ffa98 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/Playlists.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/Playlists.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.Online.Rooms; using osu.Game.Screens.OnlinePlay.Lounge; namespace osu.Game.Screens.OnlinePlay.Playlists @@ -10,5 +11,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override string ScreenTitle => "Playlists"; protected override LoungeSubScreen CreateLounge() => new PlaylistsLoungeSubScreen(); + + public void Join(Room room) => Schedule(() => Lounge.Join(room, string.Empty)); } } From c9b7aaf442c89fcf4f27cfea3171ed4b4a90d8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 May 2024 11:50:23 +0200 Subject: [PATCH 494/581] Fix formatting --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 33d2a8d348..e9fff9bb07 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Menu buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E) { - Padding = new MarginPadding { Left = WEDGE_WIDTH}, + Padding = new MarginPadding { Left = WEDGE_WIDTH }, }); buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), _ => OnEditSkin?.Invoke(), Key.S)); buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); From a4142937e75ec240b91f930614354dae5c63d9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 May 2024 12:53:25 +0200 Subject: [PATCH 495/581] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f91995feff..821a7f1fab 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ - + From 2027d481eecdd433f91f23a498ca7c7ee81dfa6c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 18 May 2024 06:38:25 +0300 Subject: [PATCH 496/581] Remove `TreatWarningsAsErrors` flags from local builds for developer convenience --- Directory.Build.props | 1 - 1 file changed, 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2d289d0f22..5ba12b845b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,6 @@ 12.0 - true enable From 0af32c5d4b0092f737bb91c3cac3498aebd40df8 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Sat, 18 May 2024 07:45:01 +0200 Subject: [PATCH 497/581] Added a minimum value for the scale calculated by the CS value. --- osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs index 2a5a11161b..1d3416f494 100644 --- a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs +++ b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Legacy // It works out to under 1 game pixel and is generally not meaningful to gameplay, but is to replay playback accuracy. const float broken_gamefield_rounding_allowance = 1.00041f; - return (float)(1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1); + return (float)Math.Max(0.02, (1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1)); } public static int CalculateDifficultyPeppyStars(BeatmapDifficulty difficulty, int objectCount, int drainLength) From a912e56ca995db8560785761209f68dcc93a0e43 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 18 May 2024 11:04:40 +0300 Subject: [PATCH 498/581] Fix checkboxes applying extra padding --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index b7b405a7e8..caab3d97f8 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -52,8 +52,6 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; - const float nub_padding = 5; - Children = new Drawable[] { LabelTextFlowContainer = new OsuTextFlowContainer(ApplyLabelParameters) @@ -69,15 +67,13 @@ namespace osu.Game.Graphics.UserInterface { Nub.Anchor = Anchor.CentreRight; Nub.Origin = Anchor.CentreRight; - Nub.Margin = new MarginPadding { Right = nub_padding }; - LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.DEFAULT_EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.DEFAULT_EXPANDED_SIZE + 10f }; } else { Nub.Anchor = Anchor.CentreLeft; Nub.Origin = Anchor.CentreLeft; - Nub.Margin = new MarginPadding { Left = nub_padding }; - LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.DEFAULT_EXPANDED_SIZE + nub_padding * 2 }; + LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.DEFAULT_EXPANDED_SIZE + 10f }; } Nub.Current.BindTo(Current); From a12a20e8b503bdb2a5647bebefe05de33b0553be Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Sat, 18 May 2024 18:37:44 +0200 Subject: [PATCH 499/581] Change Inputkeys to Ctrl+Up/Ctrl+Down --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 5dacb6db4d..b0a1684512 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -182,8 +182,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), new KeyBinding(InputKey.BackSpace, GlobalAction.DeselectAllMods), - new KeyBinding(InputKey.PageUp, GlobalAction.IncreaseSpeed), - new KeyBinding(InputKey.PageDown, GlobalAction.DecreaseSpeed), + new KeyBinding(new[] { InputKey.Control, InputKey.Up }, GlobalAction.IncreaseSpeed), + new KeyBinding(new[] { InputKey.Control, InputKey.Down }, GlobalAction.DecreaseSpeed), }; private static IEnumerable audioControlKeyBindings => new[] From 80064c4b98d955431ac423578b3a9c55b3f4ae7e Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Sat, 18 May 2024 18:38:23 +0200 Subject: [PATCH 500/581] Speedchange now also works in Modselect --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 25293e8e20..572379ea2c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,6 +27,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select; using osu.Game.Utils; using osuTK; using osuTK.Input; @@ -63,6 +64,9 @@ namespace osu.Game.Overlays.Mods private Func isValidMod = _ => true; + [Resolved] + private SongSelect? songSelect { get; set; } + /// /// A function determining whether each mod in the column should be displayed. /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. @@ -752,6 +756,14 @@ namespace osu.Game.Overlays.Mods return true; } + + case GlobalAction.IncreaseSpeed: + songSelect!.ChangeSpeed(0.05); + return true; + + case GlobalAction.DecreaseSpeed: + songSelect!.ChangeSpeed(-0.05); + return true; } return base.OnPressed(e); From 99f30d92c84789670b40ae3989cd52b2dc3a3dc2 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Sat, 18 May 2024 18:38:31 +0200 Subject: [PATCH 501/581] Add Unit Tests --- .../SongSelect/TestScenePlaySongSelect.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index e03ffd48f1..938b858110 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -87,6 +87,84 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("delete all beatmaps", () => manager.Delete()); } + [Test] + public void TestSpeedChange() + { + createSongSelect(); + changeMods(); + + AddStep("decreasing speed without mods", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.95); + + AddStep("decreasing speed with halftime", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("halftime at 0.9", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.9); + + AddStep("increasing speed with halftime", () => songSelect?.ChangeSpeed(+0.05)); + AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.95); + + AddStep("increasing speed with halftime to nomod", () => songSelect?.ChangeSpeed(+0.05)); + AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); + + AddStep("increasing speed without mods", () => songSelect?.ChangeSpeed(+0.05)); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.05); + + AddStep("increasing speed with doubletime", () => songSelect?.ChangeSpeed(+0.05)); + AddAssert("doubletime at 1.1", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.1); + + AddStep("decreasing speed with doubletime", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.05); + + OsuModNightcore nc = new OsuModNightcore + { + SpeedChange = { Value = 1.05 } + }; + changeMods(nc); + AddStep("increasing speed with nightcore", () => songSelect?.ChangeSpeed(+0.05)); + AddAssert("nightcore at 1.1", () => songSelect!.Mods.Value.Single() is ModNightcore mod && mod.SpeedChange.Value == 1.1); + + AddStep("decreasing speed with nightcore", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModNightcore mod && mod.SpeedChange.Value == 1.05); + + AddStep("decreasing speed with nightcore to nomod", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); + + AddStep("decreasing speed nomod, nightcore was selected", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.95); + + AddStep("decreasing speed with daycore", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("daycore at 0.9", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.9); + + AddStep("increasing speed with daycore", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.95); + + OsuModDoubleTime dt = new OsuModDoubleTime + { + SpeedChange = { Value = 1.02 }, + AdjustPitch = { Value = true }, + }; + changeMods(dt); + AddStep("decreasing speed from doubletime 1.02 with adjustpitch enabled", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("halftime at 0.97 with adjustpitch enabled", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.97 && mod.AdjustPitch.Value); + + OsuModHalfTime ht = new OsuModHalfTime + { + SpeedChange = { Value = 0.97 }, + AdjustPitch = { Value = true }, + }; + Mod[] modlist = { ht, new OsuModHardRock(), new OsuModHidden() }; + changeMods(modlist); + AddStep("decreasing speed from halftime 0.97 with adjustpitch enabled, HDHR enabled", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("doubletime at 1.02 with adjustpitch enabled, HDHR still enabled", () => songSelect!.Mods.Value.Count(mod => (mod is ModDoubleTime modDt && modDt.AdjustPitch.Value && modDt.SpeedChange.Value == 1.02) || mod is ModHardRock || mod is ModHidden) == 3); + + changeMods(new ModWindUp()); + AddStep("windup active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("windup still active", () => songSelect!.Mods.Value.First() is ModWindUp); + + changeMods(new ModAdaptiveSpeed()); + AddStep("adaptivespeed active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("adaptivespeed still active", () => songSelect!.Mods.Value.First() is ModAdaptiveSpeed); + } + [Test] public void TestPlaceholderBeatmapPresence() { From 3fdbd735ce063653bb92b7570df716e700a9b529 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Sat, 18 May 2024 18:40:51 +0200 Subject: [PATCH 502/581] change to single Function, Nightcore now switches into Daycore, keep Adjustpitch after change --- osu.Game/Screens/Select/SongSelect.cs | 182 +++++++++++++++----------- 1 file changed, 109 insertions(+), 73 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index de0f24aa90..e1447b284a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -98,6 +98,9 @@ namespace osu.Game.Screens.Select new OsuMenuItem(@"Select", MenuItemType.Highlighted, () => FinaliseSelection(getBeatmap())) }; + [Resolved] + private OsuGameBase? game { get; set; } + [Resolved] private Bindable> selectedMods { get; set; } = null!; @@ -144,6 +147,10 @@ namespace osu.Game.Screens.Select private Bindable configBackgroundBlur = null!; + private bool lastPitchState; + + private bool usedPitchMods; + [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog? manageCollectionsDialog, DifficultyRecommender? recommender, OsuConfigManager config) { @@ -809,94 +816,123 @@ namespace osu.Game.Screens.Select return false; } - private void increaseSpeed() + public void ChangeSpeed(double delta) { - var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); - var stateDoubleTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModDoubleTime); - bool rateModActive = ModSelect.AllAvailableMods.Count(pair => pair.Mod is ModRateAdjust && pair.Active.Value) > 0; - const double stepsize = 0.05d; - double newRate = 1d + stepsize; + // Mod Change from 0.95 DC to 1.0 none to 1.05 DT/NC ? - // If no mod rateAdjust mod is currently active activate DoubleTime with speed newRate - if (!rateModActive) - { - stateDoubleTime.Active.Value = true; - ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; + if (game == null) return; + + ModNightcore modNc = (ModNightcore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModNightcore) > 0)).Mods.First(mod => mod is ModNightcore); + ModDoubleTime modDt = (ModDoubleTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDoubleTime) > 0)).Mods.First(mod => mod is ModDoubleTime); + ModDaycore modDc = (ModDaycore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDaycore) > 0)).Mods.First(mod => mod is ModDaycore); + ModHalfTime modHt = (ModHalfTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModHalfTime) > 0)).Mods.First(mod => mod is ModHalfTime); + bool rateModActive = selectedMods.Value.Count(mod => mod is ModRateAdjust) > 0; + bool incompatiableModActive = selectedMods.Value.Count(mod => modDt.IncompatibleMods.Count(incompatibleMod => (mod.GetType().IsSubclassOf(incompatibleMod) || mod.GetType() == incompatibleMod) && incompatibleMod != typeof(ModRateAdjust)) > 0) > 0; + double newRate = 1d + delta; + bool isPositive = delta > 0; + + if (incompatiableModActive) return; - } - // Find current active rateAdjust mod and modify speed, enable DoubleTime if necessary - foreach (var state in rateAdjustStates) + if (rateModActive) { - ModRateAdjust mod = (ModRateAdjust)state.Mod; + ModRateAdjust mod = (ModRateAdjust)selectedMods.Value.First(mod => mod is ModRateAdjust); - if (!state.Active.Value) continue; + // Find current active rateAdjust mod and modify speed, enable HalfTime if necessary + newRate = mod.SpeedChange.Value + delta; - newRate = mod.SpeedChange.Value + stepsize; - - if (mod.Acronym == "DT" || mod.Acronym == "NC") - mod.SpeedChange.Value = newRate; - else + if (newRate == 1.0) { - if (newRate == 1.0d) - state.Active.Value = false; + lastPitchState = false; + usedPitchMods = false; - if (newRate > 1d) - { - state.Active.Value = false; - stateDoubleTime.Active.Value = true; - ((ModDoubleTime)stateDoubleTime.Mod).SpeedChange.Value = newRate; - break; - } + if (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) lastPitchState = true; - if (newRate < 1d) - mod.SpeedChange.Value = newRate; + if (mod is ModHalfTime htmod && htmod.AdjustPitch.Value) lastPitchState = true; + + if (mod is ModNightcore || mod is ModDaycore) usedPitchMods = true; + + //Disable RateAdjustMods + selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); + return; } - } - } - private void decreaseSpeed() - { - var rateAdjustStates = ModSelect.AllAvailableMods.Where(pair => pair.Mod is ModRateAdjust); - var stateHalfTime = ModSelect.AllAvailableMods.First(pair => pair.Mod is ModHalfTime); - bool rateModActive = ModSelect.AllAvailableMods.Count(pair => pair.Mod is ModRateAdjust && pair.Active.Value) > 0; - const double stepsize = 0.05d; - double newRate = 1d - stepsize; - - // If no mod rateAdjust mod is currently active activate HalfTime with speed newRate - if (!rateModActive) - { - stateHalfTime.Active.Value = true; - ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; - return; - } - - // Find current active rateAdjust mod and modify speed, enable HalfTime if necessary - foreach (var state in rateAdjustStates) - { - ModRateAdjust mod = (ModRateAdjust)state.Mod; - - if (!state.Active.Value) continue; - - newRate = mod.SpeedChange.Value - stepsize; - - if (mod.Acronym == "HT" || mod.Acronym == "DC") - mod.SpeedChange.Value = newRate; - else + if (((mod is ModDoubleTime || mod is ModNightcore) && newRate < mod.SpeedChange.MinValue) + || ((mod is ModHalfTime || mod is ModDaycore) && newRate > mod.SpeedChange.MaxValue)) { - if (newRate == 1.0d) - state.Active.Value = false; + bool adjustPitch = (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) || (mod is ModHalfTime htmod && htmod.AdjustPitch.Value); - if (newRate < 1d) + //Disable RateAdjustMods + selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); + + ModRateAdjust? oppositeMod = null; + + switch (mod) { - state.Active.Value = false; - stateHalfTime.Active.Value = true; - ((ModHalfTime)stateHalfTime.Mod).SpeedChange.Value = newRate; - break; + case ModDoubleTime: + modHt.AdjustPitch.Value = adjustPitch; + oppositeMod = modHt; + break; + + case ModHalfTime: + modDt.AdjustPitch.Value = adjustPitch; + oppositeMod = modDt; + break; + + case ModNightcore: + oppositeMod = modDc; + break; + + case ModDaycore: + oppositeMod = modNc; + break; } - if (newRate > 1d) - mod.SpeedChange.Value = newRate; + if (oppositeMod == null) return; + + oppositeMod.SpeedChange.Value = newRate; + selectedMods.Value = selectedMods.Value.Append(oppositeMod).ToList(); + return; + } + + if (newRate > mod.SpeedChange.MaxValue && (mod is ModDoubleTime || mod is ModNightcore)) + newRate = mod.SpeedChange.MaxValue; + + if (newRate < mod.SpeedChange.MinValue && (mod is ModHalfTime || mod is ModDaycore)) + newRate = mod.SpeedChange.MinValue; + + mod.SpeedChange.Value = newRate; + } + else + { + // If no ModRateAdjust is actived activate one + if (isPositive) + { + if (!usedPitchMods) + { + modDt.SpeedChange.Value = newRate; + modDt.AdjustPitch.Value = lastPitchState; + selectedMods.Value = selectedMods.Value.Append(modDt).ToList(); + } + else + { + modNc.SpeedChange.Value = newRate; + selectedMods.Value = selectedMods.Value.Append(modNc).ToList(); + } + } + else + { + if (!usedPitchMods) + { + modHt.SpeedChange.Value = newRate; + modHt.AdjustPitch.Value = lastPitchState; + selectedMods.Value = selectedMods.Value.Append(modHt).ToList(); + } + else + { + modDc.SpeedChange.Value = newRate; + selectedMods.Value = selectedMods.Value.Append(modDc).ToList(); + } } } } @@ -1111,11 +1147,11 @@ namespace osu.Game.Screens.Select return true; case GlobalAction.IncreaseSpeed: - increaseSpeed(); + ChangeSpeed(0.05); return true; case GlobalAction.DecreaseSpeed: - decreaseSpeed(); + ChangeSpeed(-0.05); return true; } From 614cbdf0a404487ebbeea3d98766781800a9ad0f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 18 May 2024 22:51:58 +0300 Subject: [PATCH 503/581] Reduce container nesting in PathControlPointPiece --- .../Components/PathControlPointPiece.cs | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index c6e05d3ca3..9d819f6cc0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -8,9 +8,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public readonly PathControlPoint ControlPoint; private readonly T hitObject; - private readonly Container marker; + private readonly Circle circle; private readonly Drawable markerRing; [Resolved] @@ -60,38 +60,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; - InternalChildren = new Drawable[] + InternalChildren = new[] { - marker = new Container + circle = new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new[] - { - new Circle - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20), - }, - markerRing = new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(28), - Masking = true, - BorderThickness = 2, - BorderColour = Color4.White, - Alpha = 0, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } + Size = new Vector2(20), + }, + markerRing = new CircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(28), + Alpha = 0, + InnerRadius = 0.1f, + Progress = 1 } }; } @@ -115,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } // The connecting path is excluded from positional input - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => marker.ReceivePositionalInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.ReceivePositionalInputAt(screenSpacePos); protected override bool OnHover(HoverEvent e) { @@ -209,8 +193,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (IsHovered || IsSelected.Value) colour = colour.Lighten(1); - marker.Colour = colour; - marker.Scale = new Vector2(hitObject.Scale); + Colour = colour; + Scale = new Vector2(hitObject.Scale); } private Color4 getColourFromNodeType() From be642c8c428966665fff99b0383a9d5404da801f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 May 2024 09:41:08 +0200 Subject: [PATCH 504/581] Fix total score without mods migration failing on custom ruleset scores when custom ruleset cannot be loaded Closes https://github.com/ppy/osu/issues/28209. Yes this means that such scores will have a zero total score without mods in DB and thus might up getting their total recalculated to zero when we try a mod multiplier rebalance (unless we skip scores with zero completely I suppose). I also don't really care about that right now. --- osu.Game/Database/RealmAccess.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 63f61228f3..1ece81be50 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -1134,7 +1134,17 @@ namespace osu.Game.Database case 41: foreach (var score in migration.NewRealm.All()) - LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score); + { + try + { + // this can fail e.g. if a user has a score set on a ruleset that can no longer be loaded. + LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score); + } + catch (Exception ex) + { + Logger.Log($@"Failed to populate total score without mods for score {score.ID}: {ex}", LoggingTarget.Database); + } + } break; } From e4858a975dda5a07d7b39b3b0b875167ff4cf5d1 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 May 2024 14:07:40 +0200 Subject: [PATCH 505/581] Show mouse and joystick settings on mobile --- osu.Game/OsuGameBase.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5533ee8337..5e4ec5a61d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -578,17 +578,17 @@ namespace osu.Game { case ITabletHandler th: return new TabletSettings(th); - - case MouseHandler mh: - return new MouseSettings(mh); - - case JoystickHandler jh: - return new JoystickSettings(jh); } } switch (handler) { + case MouseHandler mh: + return new MouseSettings(mh); + + case JoystickHandler jh: + return new JoystickSettings(jh); + case TouchHandler th: return new TouchSettings(th); From 04acc58b7405836a205d05c4d0e1840884385988 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 19 May 2024 14:12:21 +0200 Subject: [PATCH 506/581] Don't show warning on android Unsure about iOS. --- .../Settings/Sections/Input/MouseSettings.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 7805ed5834..6eb512fa35 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -105,12 +105,17 @@ namespace osu.Game.Overlays.Settings.Sections.Input highPrecisionMouse.Current.BindValueChanged(highPrecision => { - if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows) + switch (RuntimeInfo.OS) { - if (highPrecision.NewValue) - highPrecisionMouse.SetNoticeText(MouseSettingsStrings.HighPrecisionPlatformWarning, true); - else - highPrecisionMouse.ClearNoticeText(); + case RuntimeInfo.Platform.Linux: + case RuntimeInfo.Platform.macOS: + case RuntimeInfo.Platform.iOS: + if (highPrecision.NewValue) + highPrecisionMouse.SetNoticeText(MouseSettingsStrings.HighPrecisionPlatformWarning, true); + else + highPrecisionMouse.ClearNoticeText(); + + break; } }, true); } From 609268786f540e42996b711eab525f1e531044c4 Mon Sep 17 00:00:00 2001 From: James Wilson Date: Sun, 19 May 2024 13:28:46 +0100 Subject: [PATCH 507/581] remove unneeded extra `Previous` calls from `RhythmEvaluator` --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index 05939bb3ab..f23e8329fa 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -66,10 +66,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators } else { - if (current.Previous(i - 1).BaseObject is Slider) // bpm change is into slider, this is easy acc window + if (currObj.BaseObject is Slider) // bpm change is into slider, this is easy acc window effectiveRatio *= 0.125; - if (current.Previous(i).BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle + if (prevObj.BaseObject is Slider) // bpm change was from a slider, this is easier typically than circle -> circle effectiveRatio *= 0.25; if (previousIslandSize == islandSize) // repeated island size (ex: triplet -> triplet) From f31928875bc8510f6bfe38b95902dbb12f5e415d Mon Sep 17 00:00:00 2001 From: James Wilson Date: Sun, 19 May 2024 16:26:51 +0100 Subject: [PATCH 508/581] Reduce `Previous` calls in `RhythmEvaluator` by optimising loop logic --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index 05939bb3ab..a121b1de0b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -36,11 +36,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators while (rhythmStart < historicalNoteCount - 2 && current.StartTime - current.Previous(rhythmStart).StartTime < history_time_max) rhythmStart++; + OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)current.Previous(rhythmStart); + OsuDifficultyHitObject lastObj = (OsuDifficultyHitObject)current.Previous(rhythmStart + 1); + for (int i = rhythmStart; i > 0; i--) { OsuDifficultyHitObject currObj = (OsuDifficultyHitObject)current.Previous(i - 1); - OsuDifficultyHitObject prevObj = (OsuDifficultyHitObject)current.Previous(i); - OsuDifficultyHitObject lastObj = (OsuDifficultyHitObject)current.Previous(i + 1); double currHistoricalDecay = (history_time_max - (current.StartTime - currObj.StartTime)) / history_time_max; // scales note 0 to 1 from history to now @@ -100,6 +101,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators startRatio = effectiveRatio; islandSize = 1; } + + lastObj = prevObj; + prevObj = currObj; } return Math.Sqrt(4 + rhythmComplexitySum * rhythm_multiplier) / 2; //produces multiplier that can be applied to strain. range [1, infinity) (not really though) From c03f68413a11770c6d59f3d363dc7d16f21ebdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 09:43:25 +0200 Subject: [PATCH 509/581] Fix skin editor closest origin selection spazzing out on scaled sprites Closes https://github.com/ppy/osu/issues/28215. `drawable.Position` is a location in the parent's coordinate space, and `drawable.OriginPosition` is in the drawable's local space and additionally does not take scale into account. --- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 75bb77fa73..28b2435346 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -425,9 +425,9 @@ namespace osu.Game.Overlays.SkinEditor { if (origin == drawable.Origin) return; - var previousOrigin = drawable.OriginPosition; + var previousOrigin = drawable.ToParentSpace(drawable.OriginPosition); drawable.Origin = origin; - drawable.Position += drawable.OriginPosition - previousOrigin; + drawable.Position += drawable.ToParentSpace(drawable.OriginPosition) - previousOrigin; } private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) From 3da3b91be51526c1d40e016ddf9002134f9d788c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 10:14:08 +0200 Subject: [PATCH 510/581] Improve closest origin selection to include effects of rotation/flip Closes https://github.com/ppy/osu/issues/28237. Solution as proposed here: https://github.com/ppy/osu/pull/28089#issuecomment-2095372157 For flips and rotations by 90 degrees this does what you would expect it to. For arbitrary rotations it *sort of kind of* attempts to do this but the results are a bit wonky - probably still better than what was there before, though? --- .../SkinEditor/SkinSelectionHandler.cs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 28b2435346..21909cdc10 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -421,12 +422,41 @@ namespace osu.Game.Overlays.SkinEditor drawable.Position -= drawable.AnchorPosition - previousAnchor; } - private static void applyOrigin(Drawable drawable, Anchor origin) + private static void applyOrigin(Drawable drawable, Anchor screenSpaceOrigin) { - if (origin == drawable.Origin) return; + var boundingBox = drawable.ScreenSpaceDrawQuad.AABBFloat; + + var targetScreenSpacePosition = screenSpaceOrigin.PositionOnQuad(boundingBox); + + Anchor localOrigin = Anchor.TopLeft; + float smallestDistanceFromTargetPosition = float.PositiveInfinity; + + void checkOrigin(Anchor originToTest) + { + Vector2 positionToTest = drawable.ToScreenSpace(originToTest.PositionOnQuad(drawable.DrawRectangle)); + float testedDistance = Vector2.Distance(targetScreenSpacePosition, positionToTest); + + if (testedDistance < smallestDistanceFromTargetPosition) + { + localOrigin = originToTest; + smallestDistanceFromTargetPosition = testedDistance; + } + } + + checkOrigin(Anchor.TopLeft); + checkOrigin(Anchor.TopCentre); + checkOrigin(Anchor.TopRight); + + checkOrigin(Anchor.CentreLeft); + checkOrigin(Anchor.Centre); + checkOrigin(Anchor.CentreRight); + + checkOrigin(Anchor.BottomLeft); + checkOrigin(Anchor.BottomCentre); + checkOrigin(Anchor.BottomRight); var previousOrigin = drawable.ToParentSpace(drawable.OriginPosition); - drawable.Origin = origin; + drawable.Origin = localOrigin; drawable.Position += drawable.ToParentSpace(drawable.OriginPosition) - previousOrigin; } From fe0af7e720cf17fdbf49842c94ba2d05f6055daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 11:36:39 +0200 Subject: [PATCH 511/581] Fix unnecessary padding of empty strings for discord RPC purposes Closes https://github.com/ppy/osu/issues/28248. Destroy all software. --- osu.Desktop/DiscordRichPresence.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 3e0a9099cb..780d367900 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -273,7 +273,11 @@ namespace osu.Desktop private static string clampLength(string str) { - // For whatever reason, discord decides that strings shorter than 2 characters cannot possibly be valid input, because... reasons? + // Empty strings are fine to discord even though single-character strings are not. Make it make sense. + if (string.IsNullOrEmpty(str)) + return str; + + // As above, discord decides that *non-empty* strings shorter than 2 characters cannot possibly be valid input, because... reasons? // And yes, that is two *characters*, or *codepoints*, not *bytes* as further down below (as determined by empirical testing). // That seems very questionable, and isn't even documented anywhere. So to *make it* accept such valid input, // just tack on enough of U+200B ZERO WIDTH SPACEs at the end. From 85f85dee9ef28b4b65678d58f6e3f54abf0fe2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 14:46:28 +0200 Subject: [PATCH 512/581] Enable NRT in `TestScenePresentScore` --- .../Visual/Navigation/TestScenePresentScore.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 212783d047..2d4302c0df 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using NUnit.Framework; @@ -26,7 +24,7 @@ namespace osu.Game.Tests.Visual.Navigation { public partial class TestScenePresentScore : OsuGameTestScene { - private BeatmapSetInfo beatmap; + private BeatmapSetInfo beatmap = null!; [SetUpSteps] public new void SetUpSteps() @@ -64,7 +62,7 @@ namespace osu.Game.Tests.Visual.Navigation Ruleset = new OsuRuleset().RulesetInfo }, } - })?.Value; + })!.Value; }); } @@ -171,9 +169,9 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); } - private Func importScore(int i, RulesetInfo ruleset = null) + private Func importScore(int i, RulesetInfo? ruleset = null) { - ScoreInfo imported = null; + ScoreInfo? imported = null; AddStep($"import score {i}", () => { imported = Game.ScoreManager.Import(new ScoreInfo @@ -188,14 +186,14 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert($"import {i} succeeded", () => imported != null); - return () => imported; + return () => imported!; } /// /// Some tests test waiting for a particular screen twice in a row, but expect a new instance each time. /// There's a case where they may succeed incorrectly if we don't compare against the previous instance. /// - private IScreen lastWaitedScreen; + private IScreen lastWaitedScreen = null!; private void presentAndConfirm(Func getImport, ScorePresentType type) { From 3b15c223be4a38c624f68abd30561638d46ea14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 14:48:02 +0200 Subject: [PATCH 513/581] Add failing test case --- .../Navigation/TestScenePresentScore.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 2d4302c0df..2c2335de13 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -156,6 +156,27 @@ namespace osu.Game.Tests.Visual.Navigation presentAndConfirm(secondImport, type); } + [Test] + public void TestScoreRefetchIgnoresEmptyHash() + { + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); + AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); + + importScore(-1, hash: string.Empty); + importScore(3, hash: @"deadbeef"); + + // oftentimes a `PresentScore()` call will be given a `ScoreInfo` which is converted from an online score, + // in which cases the hash will generally not be available. + AddStep("present score", () => Game.PresentScore(new ScoreInfo { OnlineID = 3, Hash = string.Empty })); + + AddUntilStep("wait for results", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ResultsScreen); + AddUntilStep("correct score displayed", () => + { + var score = ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score!; + return score.OnlineID == 3 && score.Hash == "deadbeef"; + }); + } + private void returnToMenu() { // if we don't pause, there's a chance the track may change at the main menu out of our control (due to reaching the end of the track). @@ -169,14 +190,14 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("wait for menu", () => Game.ScreenStack.CurrentScreen is MainMenu); } - private Func importScore(int i, RulesetInfo? ruleset = null) + private Func importScore(int i, RulesetInfo? ruleset = null, string? hash = null) { ScoreInfo? imported = null; AddStep($"import score {i}", () => { imported = Game.ScoreManager.Import(new ScoreInfo { - Hash = Guid.NewGuid().ToString(), + Hash = hash ?? Guid.NewGuid().ToString(), OnlineID = i, BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = ruleset ?? new OsuRuleset().RulesetInfo, From ed498f6eed66a4c801604484dbcbea883b229cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 May 2024 14:48:36 +0200 Subject: [PATCH 514/581] Do not attempt to match score by equality of hash if it's empty Closes https://github.com/ppy/osu/issues/28216. The affected user's database contained six sentakki scores with an empty hash. When an online score is being imported, an online model (which does not have a hash) will be transmogrified into a `ScoreInfo` with an empty hash, which would end up accidentally matching those scores and basically breaking everything at that point. To fix, avoid attempting to match anything on empty hash. This does not break online score matching because for those cases the actual online ID of the score will be used. --- osu.Game/Scoring/ScoreManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 0c707ffa19..df4735b5e6 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -88,7 +88,7 @@ namespace osu.Game.Scoring { ScoreInfo? databasedScoreInfo = null; - if (originalScoreInfo is ScoreInfo scoreInfo) + if (originalScoreInfo is ScoreInfo scoreInfo && !string.IsNullOrEmpty(scoreInfo.Hash)) databasedScoreInfo = Query(s => s.Hash == scoreInfo.Hash); if (originalScoreInfo.OnlineID > 0) From db8b72eb37464d50ec5092f7180082507e7fc2b0 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Mon, 20 May 2024 16:23:16 +0200 Subject: [PATCH 515/581] Clamped Difficulty Ranges to [0,10] --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 6fa78fa8e6..059451e228 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -383,21 +383,24 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"HPDrainRate": - difficulty.DrainRate = Parsing.ParseFloat(pair.Value); + difficulty.DrainRate = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); break; case @"CircleSize": difficulty.CircleSize = Parsing.ParseFloat(pair.Value); + //If the mode is not Mania, clamp circle size to [0,10] + if (!beatmap.BeatmapInfo.Ruleset.OnlineID.Equals(3)) + difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 0, 10); break; case @"OverallDifficulty": - difficulty.OverallDifficulty = Parsing.ParseFloat(pair.Value); + difficulty.OverallDifficulty = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); if (!hasApproachRate) difficulty.ApproachRate = difficulty.OverallDifficulty; break; case @"ApproachRate": - difficulty.ApproachRate = Parsing.ParseFloat(pair.Value); + difficulty.ApproachRate = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); hasApproachRate = true; break; From e740b8bcc358653d12ff528fb33e6c42cf505559 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2024 14:36:11 +0800 Subject: [PATCH 516/581] Fix single frame glitching in skin editor https://github.com/ppy/osu/pull/28257#discussion_r1606999574 --- osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 21909cdc10..8fd9c1b559 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -455,9 +455,10 @@ namespace osu.Game.Overlays.SkinEditor checkOrigin(Anchor.BottomCentre); checkOrigin(Anchor.BottomRight); - var previousOrigin = drawable.ToParentSpace(drawable.OriginPosition); + Vector2 offset = drawable.ToParentSpace(localOrigin.PositionOnQuad(drawable.DrawRectangle)) - drawable.ToParentSpace(drawable.Origin.PositionOnQuad(drawable.DrawRectangle)); + drawable.Origin = localOrigin; - drawable.Position += drawable.ToParentSpace(drawable.OriginPosition) - previousOrigin; + drawable.Position += offset; } private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) From d7d569cf4e68acdbcc9cec844337f93bdb207a54 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 May 2024 14:34:53 +0800 Subject: [PATCH 517/581] Temporary rollback of framework / SDL3 --- osu.Android.props | 2 +- osu.Android/AndroidJoystickSettings.cs | 76 +++++++++++++++ osu.Android/AndroidMouseSettings.cs | 97 +++++++++++++++++++ osu.Android/OsuGameAndroid.cs | 22 +++++ osu.Desktop/OsuGameDesktop.cs | 11 +-- osu.Desktop/Program.cs | 33 +++---- .../Components/PathControlPointVisualiser.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 +- .../Screens/Ladder/LadderDragContainer.cs | 2 +- osu.Game/Database/EmptyRealmSet.cs | 5 - .../UserInterface/ExpandableSlider.cs | 8 +- .../Graphics/UserInterface/OsuSliderBar.cs | 9 +- .../Graphics/UserInterface/OsuTabControl.cs | 24 ++--- .../Graphics/UserInterface/PageTabControl.cs | 14 +-- .../UserInterface/RoundedSliderBar.cs | 5 +- .../UserInterface/ShearedSliderBar.cs | 5 +- .../UserInterfaceV2/LabelledSliderBar.cs | 4 +- .../UserInterfaceV2/SliderWithTextBoxInput.cs | 8 +- osu.Game/OsuGameBase.cs | 12 +-- .../BeatmapListingCardSizeTabControl.cs | 12 +-- ...BeatmapSearchMultipleSelectionFilterRow.cs | 4 - .../Overlays/BeatmapListing/FilterTabItem.cs | 12 +-- .../BeatmapSet/BeatmapRulesetSelector.cs | 2 +- .../OverlayPanelDisplayStyleControl.cs | 14 +-- osu.Game/Overlays/OverlayRulesetTabItem.cs | 14 +-- osu.Game/Overlays/OverlayStreamItem.cs | 12 +-- osu.Game/Overlays/OverlayTabControl.cs | 14 +-- .../Overlays/Settings/Sections/SizeSlider.cs | 3 +- .../Settings/SettingsPercentageSlider.cs | 4 +- osu.Game/Overlays/Settings/SettingsSlider.cs | 6 +- .../Toolbar/ToolbarRulesetSelector.cs | 16 ++- .../Toolbar/ToolbarRulesetTabButton.cs | 12 --- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 3 +- .../Scoring/LegacyDrainingHealthProcessor.cs | 7 -- .../Rulesets/UI/FrameStabilityContainer.cs | 2 +- .../Timeline/TimelineTickDisplay.cs | 3 +- osu.Game/Screens/Edit/Editor.cs | 14 ++- .../IndeterminateSliderWithTextBoxInput.cs | 8 +- .../Match/Components/MatchTypePicker.cs | 11 +-- .../Play/PlayerSettings/PlayerSliderBar.cs | 4 +- osu.Game/osu.Game.csproj | 4 +- osu.iOS.props | 2 +- 44 files changed, 304 insertions(+), 226 deletions(-) create mode 100644 osu.Android/AndroidJoystickSettings.cs create mode 100644 osu.Android/AndroidMouseSettings.cs diff --git a/osu.Android.props b/osu.Android.props index e20ac2e0b7..2d7a9d2652 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 103ef50e0c..b2e3fc0779 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 1127a69359f6eb9305d74a85dff8579278135997 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Tue, 21 May 2024 10:15:53 +0200 Subject: [PATCH 518/581] Moved DIfficulty Clamping to occur after the file has been parsed This is to handle potential issues with the ruleset being parsed after circle size has been parsed. --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 059451e228..2acabe2518 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -85,6 +85,8 @@ namespace osu.Game.Beatmaps.Formats base.ParseStreamInto(stream, beatmap); + applyDifficultyRestrictions(beatmap.Difficulty); + flushPendingPoints(); // Objects may be out of order *only* if a user has manually edited an .osu file. @@ -102,6 +104,26 @@ namespace osu.Game.Beatmaps.Formats } } + /// + /// Clamp Difficulty settings to be within the normal range. + /// + private void applyDifficultyRestrictions(BeatmapDifficulty difficulty) + { + difficulty.DrainRate = Math.Clamp(difficulty.DrainRate, 0, 10); + //If the mode is not Mania, clamp circle size to [0,10] + if (!beatmap.BeatmapInfo.Ruleset.OnlineID.Equals(3)) + difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 0, 10); + //If it is Mania, it must be within [1,20] - dual stages with 10 keys each. + //The lower bound should be 4, but there are ranked maps that are lower than this. + else + difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 1, 20); + difficulty.OverallDifficulty = Math.Clamp(difficulty.OverallDifficulty, 0, 10); + difficulty.ApproachRate = Math.Clamp(difficulty.ApproachRate, 0, 10); + + difficulty.SliderMultiplier = Math.Clamp(difficulty.SliderMultiplier, 0.4, 3.6); + difficulty.SliderTickRate = Math.Clamp(difficulty.SliderTickRate, 0.5, 8); + } + /// /// Processes the beatmap such that a new combo is started the first hitobject following each break. /// @@ -383,33 +405,30 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"HPDrainRate": - difficulty.DrainRate = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); + difficulty.DrainRate = Parsing.ParseFloat(pair.Value); break; case @"CircleSize": difficulty.CircleSize = Parsing.ParseFloat(pair.Value); - //If the mode is not Mania, clamp circle size to [0,10] - if (!beatmap.BeatmapInfo.Ruleset.OnlineID.Equals(3)) - difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 0, 10); break; case @"OverallDifficulty": - difficulty.OverallDifficulty = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); + difficulty.OverallDifficulty = Parsing.ParseFloat(pair.Value); if (!hasApproachRate) difficulty.ApproachRate = difficulty.OverallDifficulty; break; case @"ApproachRate": - difficulty.ApproachRate = Math.Clamp(Parsing.ParseFloat(pair.Value), 0, 10); + difficulty.ApproachRate = Parsing.ParseFloat(pair.Value); hasApproachRate = true; break; case @"SliderMultiplier": - difficulty.SliderMultiplier = Math.Clamp(Parsing.ParseDouble(pair.Value), 0.4, 3.6); + difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); break; case @"SliderTickRate": - difficulty.SliderTickRate = Math.Clamp(Parsing.ParseDouble(pair.Value), 0.5, 8); + difficulty.SliderTickRate = Parsing.ParseDouble(pair.Value); break; } } From 45fcbea182d1076ae9239984f76ed9b36b458c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 May 2024 14:43:06 +0200 Subject: [PATCH 519/581] Compute total score without mods during standardised score conversion This is going to be used by server-side flows. Note that the server-side overload of `UpdateFromLegacy()` was not calling `LegacyScoreDecoder.PopulateTotalScoreWithoutMods()`. Computing the score without mods inline reduces reflection overheads from constructing mod instances, which feels pretty important for server-side flows. There is one weird kink in the treatment of stable scores with score V2 active - they get the *legacy* multipliers unapplied for them because that made the most sense. For all intents and purposes this matters mostly for client-side replays with score V2. I'm not sure whether scores with SV2 ever make it to submission in stable. There may be minute differences in converted score due to rounding shenanigans but I don't think it's worth doing a reverify for this. --- .../StandardisedScoreMigrationTools.cs | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 7d09ebdb40..db44731bed 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -16,7 +16,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Scoring; -using osu.Game.Scoring.Legacy; namespace osu.Game.Database { @@ -248,8 +247,7 @@ namespace osu.Game.Database // warning: ordering is important here - both total score and ranks are dependent on accuracy! score.Accuracy = computeAccuracy(score, scoreProcessor); score.Rank = computeRank(score, scoreProcessor); - score.TotalScore = convertFromLegacyTotalScore(score, ruleset, beatmap); - LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score); + (score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, beatmap); } /// @@ -273,7 +271,7 @@ namespace osu.Game.Database // warning: ordering is important here - both total score and ranks are dependent on accuracy! score.Accuracy = computeAccuracy(score, scoreProcessor); score.Rank = computeRank(score, scoreProcessor); - score.TotalScore = convertFromLegacyTotalScore(score, ruleset, difficulty, attributes); + (score.TotalScoreWithoutMods, score.TotalScore) = convertFromLegacyTotalScore(score, ruleset, difficulty, attributes); } /// @@ -283,17 +281,13 @@ namespace osu.Game.Database /// The in which the score was set. /// The applicable for this score. /// The standardised total score. - private static long convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, WorkingBeatmap beatmap) + private static (long withoutMods, long withMods) convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, WorkingBeatmap beatmap) { if (!score.IsLegacyScore) - return score.TotalScore; + return (score.TotalScoreWithoutMods, score.TotalScore); if (ruleset is not ILegacyRuleset legacyRuleset) - return score.TotalScore; - - var mods = score.Mods; - if (mods.Any(mod => mod is ModScoreV2)) - return score.TotalScore; + return (score.TotalScoreWithoutMods, score.TotalScore); var playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods); @@ -302,8 +296,13 @@ namespace osu.Game.Database ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator(); LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap); + var legacyBeatmapConversionDifficultyInfo = LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap); - return convertFromLegacyTotalScore(score, ruleset, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); + var mods = score.Mods; + if (mods.Any(mod => mod is ModScoreV2)) + return ((long)Math.Round(score.TotalScore / sv1Simulator.GetLegacyScoreMultiplier(mods, legacyBeatmapConversionDifficultyInfo)), score.TotalScore); + + return convertFromLegacyTotalScore(score, ruleset, legacyBeatmapConversionDifficultyInfo, attributes); } /// @@ -314,15 +313,15 @@ namespace osu.Game.Database /// The beatmap difficulty. /// The legacy scoring attributes for the beatmap which the score was set on. /// The standardised total score. - private static long convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) + private static (long withoutMods, long withMods) convertFromLegacyTotalScore(ScoreInfo score, Ruleset ruleset, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) { if (!score.IsLegacyScore) - return score.TotalScore; + return (score.TotalScoreWithoutMods, score.TotalScore); Debug.Assert(score.LegacyTotalScore != null); if (ruleset is not ILegacyRuleset legacyRuleset) - return score.TotalScore; + return (score.TotalScoreWithoutMods, score.TotalScore); double legacyModMultiplier = legacyRuleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(score.Mods, difficulty); int maximumLegacyAccuracyScore = attributes.AccuracyScore; @@ -354,17 +353,18 @@ namespace osu.Game.Database double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); - long convertedTotalScore; + long convertedTotalScoreWithoutMods; switch (score.Ruleset.OnlineID) { case 0: if (score.MaxCombo == 0 || score.Accuracy == 0) { - return (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( 0 + 500000 * Math.Pow(score.Accuracy, 5) - + bonusProportion) * modMultiplier); + + bonusProportion); + break; } // see similar check above. @@ -372,10 +372,11 @@ namespace osu.Game.Database // are either pointless or wildly wrong. if (maximumLegacyComboScore + maximumLegacyBonusScore == 0) { - return (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( 500000 * comboProportion // as above, zero if mods result in zero multiplier, one otherwise + 500000 * Math.Pow(score.Accuracy, 5) - + bonusProportion) * modMultiplier); + + bonusProportion); + break; } // Assumptions: @@ -472,17 +473,17 @@ namespace osu.Game.Database double newComboScoreProportion = estimatedComboPortionInStandardisedScore / maximumAchievableComboPortionInStandardisedScore; - convertedTotalScore = (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( 500000 * newComboScoreProportion * score.Accuracy + 500000 * Math.Pow(score.Accuracy, 5) - + bonusProportion) * modMultiplier); + + bonusProportion); break; case 1: - convertedTotalScore = (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( 250000 * comboProportion + 750000 * Math.Pow(score.Accuracy, 3.6) - + bonusProportion) * modMultiplier); + + bonusProportion); break; case 2: @@ -507,28 +508,28 @@ namespace osu.Game.Database ? 0 : (double)score.Statistics.GetValueOrDefault(HitResult.SmallTickHit) / score.MaximumStatistics.GetValueOrDefault(HitResult.SmallTickHit); - convertedTotalScore = (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( comboPortion * estimateComboProportionForCatch(attributes.MaxCombo, score.MaxCombo, score.Statistics.GetValueOrDefault(HitResult.Miss)) + dropletsPortion * dropletsHit - + bonusProportion) * modMultiplier); + + bonusProportion); break; case 3: - convertedTotalScore = (long)Math.Round(( + convertedTotalScoreWithoutMods = (long)Math.Round( 850000 * comboProportion + 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy) - + bonusProportion) * modMultiplier); + + bonusProportion); break; default: - convertedTotalScore = score.TotalScore; - break; + return (score.TotalScoreWithoutMods, score.TotalScore); } - if (convertedTotalScore < 0) - throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScore}"); + if (convertedTotalScoreWithoutMods < 0) + throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScoreWithoutMods}"); - return convertedTotalScore; + long convertedTotalScore = (long)Math.Round(convertedTotalScoreWithoutMods * modMultiplier); + return (convertedTotalScoreWithoutMods, convertedTotalScore); } /// From 148afd120127c58655ed35190fb7456ce1f0e973 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Tue, 21 May 2024 14:47:34 +0200 Subject: [PATCH 520/581] Change Speedchange behaviour to keep changing while holding key, Add Toast to nofity user what just happend --- osu.Game/Localisation/ToastStrings.cs | 5 +++++ osu.Game/Overlays/OSD/SpeedChangeToast.cs | 17 +++++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 26 ++++++++++++++++------- 3 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Overlays/OSD/SpeedChangeToast.cs diff --git a/osu.Game/Localisation/ToastStrings.cs b/osu.Game/Localisation/ToastStrings.cs index da798a3937..33027966dd 100644 --- a/osu.Game/Localisation/ToastStrings.cs +++ b/osu.Game/Localisation/ToastStrings.cs @@ -49,6 +49,11 @@ namespace osu.Game.Localisation /// public static LocalisableString UrlCopied => new TranslatableString(getKey(@"url_copied"), @"URL copied"); + /// + /// "Speed Changed" + /// + public static LocalisableString SpeedChanged => new TranslatableString(getKey(@"speed_changed"), @"Speed Changed"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/OSD/SpeedChangeToast.cs b/osu.Game/Overlays/OSD/SpeedChangeToast.cs new file mode 100644 index 0000000000..73ba23622b --- /dev/null +++ b/osu.Game/Overlays/OSD/SpeedChangeToast.cs @@ -0,0 +1,17 @@ +// 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.Configuration; +using osu.Game.Input.Bindings; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.OSD +{ + public partial class SpeedChangeToast : Toast + { + public SpeedChangeToast(OsuConfigManager config, double delta) + : base(CommonStrings.Beatmaps, ToastStrings.SpeedChanged, config.LookupKeyBindings(GlobalAction.IncreaseSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseSpeed)) + { + } + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e1447b284a..7eb2be9100 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -30,6 +30,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Overlays.OSD; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Backgrounds; @@ -151,6 +152,12 @@ namespace osu.Game.Screens.Select private bool usedPitchMods; + [Resolved] + private OnScreenDisplay? onScreenDisplay { get; set; } + + [Resolved] + private OsuConfigManager? config { get; set; } + [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog? manageCollectionsDialog, DifficultyRecommender? recommender, OsuConfigManager config) { @@ -819,7 +826,7 @@ namespace osu.Game.Screens.Select public void ChangeSpeed(double delta) { // Mod Change from 0.95 DC to 1.0 none to 1.05 DT/NC ? - + onScreenDisplay?.Display(new SpeedChangeToast(config!, delta)); if (game == null) return; ModNightcore modNc = (ModNightcore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModNightcore) > 0)).Mods.First(mod => mod is ModNightcore); @@ -1135,17 +1142,10 @@ namespace osu.Game.Screens.Select public virtual bool OnPressed(KeyBindingPressEvent e) { - if (e.Repeat) - return false; - if (!this.IsCurrentScreen()) return false; switch (e.Action) { - case GlobalAction.Select: - FinaliseSelection(); - return true; - case GlobalAction.IncreaseSpeed: ChangeSpeed(0.05); return true; @@ -1155,6 +1155,16 @@ namespace osu.Game.Screens.Select return true; } + if (e.Repeat) + return false; + + switch (e.Action) + { + case GlobalAction.Select: + FinaliseSelection(); + return true; + } + return false; } From 3403789c6fef04827679b27715b653778b7d0aed Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Tue, 21 May 2024 16:11:20 +0200 Subject: [PATCH 521/581] Toast now only shows when speed is actually changed --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7eb2be9100..b78134392b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -825,8 +825,6 @@ namespace osu.Game.Screens.Select public void ChangeSpeed(double delta) { - // Mod Change from 0.95 DC to 1.0 none to 1.05 DT/NC ? - onScreenDisplay?.Display(new SpeedChangeToast(config!, delta)); if (game == null) return; ModNightcore modNc = (ModNightcore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModNightcore) > 0)).Mods.First(mod => mod is ModNightcore); @@ -841,6 +839,8 @@ namespace osu.Game.Screens.Select if (incompatiableModActive) return; + onScreenDisplay?.Display(new SpeedChangeToast(config!, delta)); + if (rateModActive) { ModRateAdjust mod = (ModRateAdjust)selectedMods.Value.First(mod => mod is ModRateAdjust); From 99d99cede03993796aa4f8fe5f1d344a9cfe9472 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 11:59:34 +0800 Subject: [PATCH 522/581] Basic cleanup Before I gave up on attempting to fix the method. --- osu.Game/Screens/Select/SongSelect.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b78134392b..18d5799bae 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select }; [Resolved] - private OsuGameBase? game { get; set; } + private OsuGameBase game { get; set; } = null!; [Resolved] private Bindable> selectedMods { get; set; } = null!; @@ -156,7 +156,7 @@ namespace osu.Game.Screens.Select private OnScreenDisplay? onScreenDisplay { get; set; } [Resolved] - private OsuConfigManager? config { get; set; } + private OsuConfigManager config { get; set; } = null!; [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog? manageCollectionsDialog, DifficultyRecommender? recommender, OsuConfigManager config) @@ -825,21 +825,19 @@ namespace osu.Game.Screens.Select public void ChangeSpeed(double delta) { - if (game == null) return; - ModNightcore modNc = (ModNightcore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModNightcore) > 0)).Mods.First(mod => mod is ModNightcore); ModDoubleTime modDt = (ModDoubleTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDoubleTime) > 0)).Mods.First(mod => mod is ModDoubleTime); ModDaycore modDc = (ModDaycore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDaycore) > 0)).Mods.First(mod => mod is ModDaycore); ModHalfTime modHt = (ModHalfTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModHalfTime) > 0)).Mods.First(mod => mod is ModHalfTime); bool rateModActive = selectedMods.Value.Count(mod => mod is ModRateAdjust) > 0; - bool incompatiableModActive = selectedMods.Value.Count(mod => modDt.IncompatibleMods.Count(incompatibleMod => (mod.GetType().IsSubclassOf(incompatibleMod) || mod.GetType() == incompatibleMod) && incompatibleMod != typeof(ModRateAdjust)) > 0) > 0; + bool incompatibleModActive = selectedMods.Value.Count(mod => modDt.IncompatibleMods.Count(incompatibleMod => (mod.GetType().IsSubclassOf(incompatibleMod) || mod.GetType() == incompatibleMod) && incompatibleMod != typeof(ModRateAdjust)) > 0) > 0; double newRate = 1d + delta; bool isPositive = delta > 0; - if (incompatiableModActive) + if (incompatibleModActive) return; - onScreenDisplay?.Display(new SpeedChangeToast(config!, delta)); + onScreenDisplay?.Display(new SpeedChangeToast(config, delta)); if (rateModActive) { @@ -912,7 +910,7 @@ namespace osu.Game.Screens.Select } else { - // If no ModRateAdjust is actived activate one + // If no ModRateAdjust is active, activate one if (isPositive) { if (!usedPitchMods) From 02a388cba6493207a170728abd607d80bfdecb3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 12:03:48 +0800 Subject: [PATCH 523/581] Fix enum not being at end (and adjust naming) --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 16 ++++++++-------- .../GlobalActionKeyBindingStrings.cs | 8 ++++---- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/OSD/SpeedChangeToast.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b0a1684512..09db7461d6 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -182,8 +182,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.SelectPreviousRandom), new KeyBinding(InputKey.F3, GlobalAction.ToggleBeatmapOptions), new KeyBinding(InputKey.BackSpace, GlobalAction.DeselectAllMods), - new KeyBinding(new[] { InputKey.Control, InputKey.Up }, GlobalAction.IncreaseSpeed), - new KeyBinding(new[] { InputKey.Control, InputKey.Down }, GlobalAction.DecreaseSpeed), + new KeyBinding(new[] { InputKey.Control, InputKey.Up }, GlobalAction.IncreaseModSpeed), + new KeyBinding(new[] { InputKey.Control, InputKey.Down }, GlobalAction.DecreaseModSpeed), }; private static IEnumerable audioControlKeyBindings => new[] @@ -411,12 +411,6 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))] EditorToggleRotateControl, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseSpeed))] - IncreaseSpeed, - - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseSpeed))] - DecreaseSpeed, - [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))] IncreaseOffset, @@ -428,6 +422,12 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.StepReplayBackward))] StepReplayBackward, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseModSpeed))] + IncreaseModSpeed, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseModSpeed))] + DecreaseModSpeed, } public enum GlobalActionCategory diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index d0cbf52f07..18a1d3e4fe 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -370,14 +370,14 @@ namespace osu.Game.Localisation public static LocalisableString EditorToggleRotateControl => new TranslatableString(getKey(@"editor_toggle_rotate_control"), @"Toggle rotate control"); /// - /// "Increase Speed" + /// "Increase mod speed" /// - public static LocalisableString IncreaseSpeed => new TranslatableString(getKey(@"increase_speed"), @"Increase Speed"); + public static LocalisableString IncreaseModSpeed => new TranslatableString(getKey(@"increase_mod_speed"), @"Increase mod speed"); /// - /// "Decrease Speed" + /// "Decrease mod speed" /// - public static LocalisableString DecreaseSpeed => new TranslatableString(getKey(@"decrease_speed"), @"Decrease Speed"); + public static LocalisableString DecreaseModSpeed => new TranslatableString(getKey(@"decrease_mod_speed"), @"Decrease mod speed"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 572379ea2c..3b8090a4b2 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -757,11 +757,11 @@ namespace osu.Game.Overlays.Mods return true; } - case GlobalAction.IncreaseSpeed: + case GlobalAction.IncreaseModSpeed: songSelect!.ChangeSpeed(0.05); return true; - case GlobalAction.DecreaseSpeed: + case GlobalAction.DecreaseModSpeed: songSelect!.ChangeSpeed(-0.05); return true; } diff --git a/osu.Game/Overlays/OSD/SpeedChangeToast.cs b/osu.Game/Overlays/OSD/SpeedChangeToast.cs index 73ba23622b..231ef86526 100644 --- a/osu.Game/Overlays/OSD/SpeedChangeToast.cs +++ b/osu.Game/Overlays/OSD/SpeedChangeToast.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.OSD public partial class SpeedChangeToast : Toast { public SpeedChangeToast(OsuConfigManager config, double delta) - : base(CommonStrings.Beatmaps, ToastStrings.SpeedChanged, config.LookupKeyBindings(GlobalAction.IncreaseSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseSpeed)) + : base(CommonStrings.Beatmaps, ToastStrings.SpeedChanged, config.LookupKeyBindings(GlobalAction.IncreaseModSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseModSpeed)) { } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 18d5799bae..257f6583a4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -1144,11 +1144,11 @@ namespace osu.Game.Screens.Select switch (e.Action) { - case GlobalAction.IncreaseSpeed: + case GlobalAction.IncreaseModSpeed: ChangeSpeed(0.05); return true; - case GlobalAction.DecreaseSpeed: + case GlobalAction.DecreaseModSpeed: ChangeSpeed(-0.05); return true; } From f979200712aa0336de04e30fb2b3bebd714b5920 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 12:06:51 +0800 Subject: [PATCH 524/581] Use null conditional rather than implicit not-null --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 3b8090a4b2..ad589e8fa9 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -758,11 +758,11 @@ namespace osu.Game.Overlays.Mods } case GlobalAction.IncreaseModSpeed: - songSelect!.ChangeSpeed(0.05); + songSelect?.ChangeSpeed(0.05); return true; case GlobalAction.DecreaseModSpeed: - songSelect!.ChangeSpeed(-0.05); + songSelect?.ChangeSpeed(-0.05); return true; } From d0b1ebff5a616ca89391a87699df320edbb695a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 16:29:39 +0800 Subject: [PATCH 525/581] Revert "Temporary rollback of framework / SDL3" This reverts commit d7d569cf4e68acdbcc9cec844337f93bdb207a54. --- osu.Android.props | 2 +- osu.Android/AndroidJoystickSettings.cs | 76 --------------- osu.Android/AndroidMouseSettings.cs | 97 ------------------- osu.Android/OsuGameAndroid.cs | 22 ----- osu.Desktop/OsuGameDesktop.cs | 11 ++- osu.Desktop/Program.cs | 33 ++++--- .../Components/PathControlPointVisualiser.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 +- .../Screens/Ladder/LadderDragContainer.cs | 2 +- osu.Game/Database/EmptyRealmSet.cs | 5 + .../UserInterface/ExpandableSlider.cs | 8 +- .../Graphics/UserInterface/OsuSliderBar.cs | 9 +- .../Graphics/UserInterface/OsuTabControl.cs | 24 +++-- .../Graphics/UserInterface/PageTabControl.cs | 14 ++- .../UserInterface/RoundedSliderBar.cs | 5 +- .../UserInterface/ShearedSliderBar.cs | 5 +- .../UserInterfaceV2/LabelledSliderBar.cs | 4 +- .../UserInterfaceV2/SliderWithTextBoxInput.cs | 8 +- osu.Game/OsuGameBase.cs | 12 ++- .../BeatmapListingCardSizeTabControl.cs | 12 ++- ...BeatmapSearchMultipleSelectionFilterRow.cs | 4 + .../Overlays/BeatmapListing/FilterTabItem.cs | 12 ++- .../BeatmapSet/BeatmapRulesetSelector.cs | 2 +- .../OverlayPanelDisplayStyleControl.cs | 14 ++- osu.Game/Overlays/OverlayRulesetTabItem.cs | 14 ++- osu.Game/Overlays/OverlayStreamItem.cs | 12 ++- osu.Game/Overlays/OverlayTabControl.cs | 14 ++- .../Overlays/Settings/Sections/SizeSlider.cs | 3 +- .../Settings/SettingsPercentageSlider.cs | 4 +- osu.Game/Overlays/Settings/SettingsSlider.cs | 6 +- .../Toolbar/ToolbarRulesetSelector.cs | 16 +-- .../Toolbar/ToolbarRulesetTabButton.cs | 12 +++ osu.Game/Rulesets/Mods/DifficultyBindable.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 3 +- .../Scoring/LegacyDrainingHealthProcessor.cs | 7 ++ .../Rulesets/UI/FrameStabilityContainer.cs | 2 +- .../Timeline/TimelineTickDisplay.cs | 3 +- osu.Game/Screens/Edit/Editor.cs | 14 +-- .../IndeterminateSliderWithTextBoxInput.cs | 8 +- .../Match/Components/MatchTypePicker.cs | 11 ++- .../Play/PlayerSettings/PlayerSliderBar.cs | 4 +- osu.Game/osu.Game.csproj | 4 +- osu.iOS.props | 2 +- 44 files changed, 226 insertions(+), 304 deletions(-) delete mode 100644 osu.Android/AndroidJoystickSettings.cs delete mode 100644 osu.Android/AndroidMouseSettings.cs diff --git a/osu.Android.props b/osu.Android.props index 2d7a9d2652..e20ac2e0b7 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index b2e3fc0779..103ef50e0c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From b25987ffe726cd5815aa8f84919180db47a88ddc Mon Sep 17 00:00:00 2001 From: Aurelian Date: Wed, 22 May 2024 11:37:55 +0200 Subject: [PATCH 526/581] Changed allowed mania keys, and reverted 0af32c5 --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +++--- osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 2acabe2518..e5567b2215 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -111,12 +111,12 @@ namespace osu.Game.Beatmaps.Formats { difficulty.DrainRate = Math.Clamp(difficulty.DrainRate, 0, 10); //If the mode is not Mania, clamp circle size to [0,10] - if (!beatmap.BeatmapInfo.Ruleset.OnlineID.Equals(3)) + if (beatmap.BeatmapInfo.Ruleset.OnlineID != 3) difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 0, 10); - //If it is Mania, it must be within [1,20] - dual stages with 10 keys each. + //If it is Mania, it must be within [1,18] - copying what stable does https://github.com/ppy/osu/pull/28200#discussion_r1609522988 //The lower bound should be 4, but there are ranked maps that are lower than this. else - difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 1, 20); + difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 1, 18); difficulty.OverallDifficulty = Math.Clamp(difficulty.OverallDifficulty, 0, 10); difficulty.ApproachRate = Math.Clamp(difficulty.ApproachRate, 0, 10); diff --git a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs index 1d3416f494..2a5a11161b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs +++ b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Legacy // It works out to under 1 game pixel and is generally not meaningful to gameplay, but is to replay playback accuracy. const float broken_gamefield_rounding_allowance = 1.00041f; - return (float)Math.Max(0.02, (1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1)); + return (float)(1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1); } public static int CalculateDifficultyPeppyStars(BeatmapDifficulty difficulty, int objectCount, int drainLength) From 97fe59cb24cc2f26ff2d099ef6da09c95d9fd6ba Mon Sep 17 00:00:00 2001 From: tsunyoku Date: Wed, 22 May 2024 10:38:47 +0100 Subject: [PATCH 527/581] set `Ranked` to `true` for `OsuModTraceable` --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 9671f53bea..75ad00e169 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; + public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) }; From f3cae73e2ed892469e1879f834f4c8472e06cf13 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Wed, 22 May 2024 13:26:00 +0200 Subject: [PATCH 528/581] Added tests for difficulty clamping --- .../Formats/LegacyBeatmapDecoderTest.cs | 30 +++++++++++++++++++ .../out-of-range-difficulties-mania.osu | 5 ++++ .../Resources/out-of-range-difficulties.osu | 10 +++++++ 3 files changed, 45 insertions(+) create mode 100644 osu.Game.Tests/Resources/out-of-range-difficulties-mania.osu create mode 100644 osu.Game.Tests/Resources/out-of-range-difficulties.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 02432a1935..e6daba2016 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1188,5 +1188,35 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(beatmap.HitObjects[0].GetEndTime(), Is.EqualTo(3153)); } } + + [Test] + public void TestBeatmapDifficultyIsClamped() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("out-of-range-difficulties.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream).Difficulty; + Assert.That(decoded.DrainRate, Is.EqualTo(10)); + Assert.That(decoded.CircleSize, Is.EqualTo(10)); + Assert.That(decoded.OverallDifficulty, Is.EqualTo(10)); + Assert.That(decoded.ApproachRate, Is.EqualTo(10)); + Assert.That(decoded.SliderMultiplier, Is.EqualTo(3.6)); + Assert.That(decoded.SliderTickRate, Is.EqualTo(8)); + } + } + [Test] + public void TestManiaBeatmapDifficultyCircleSizeClamp() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("out-of-range-difficulties-mania.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream).Difficulty; + Assert.That(decoded.CircleSize, Is.EqualTo(14)); + } + } } } diff --git a/osu.Game.Tests/Resources/out-of-range-difficulties-mania.osu b/osu.Game.Tests/Resources/out-of-range-difficulties-mania.osu new file mode 100644 index 0000000000..7dc2e51ad9 --- /dev/null +++ b/osu.Game.Tests/Resources/out-of-range-difficulties-mania.osu @@ -0,0 +1,5 @@ +[General] +Mode: 3 + +[Difficulty] +CircleSize:14 \ No newline at end of file diff --git a/osu.Game.Tests/Resources/out-of-range-difficulties.osu b/osu.Game.Tests/Resources/out-of-range-difficulties.osu new file mode 100644 index 0000000000..5029395614 --- /dev/null +++ b/osu.Game.Tests/Resources/out-of-range-difficulties.osu @@ -0,0 +1,10 @@ +[General] +Mode: 0 + +[Difficulty] +HPDrainRate:25 +CircleSize:25 +OverallDifficulty:25 +ApproachRate:30 +SliderMultiplier:30 +SliderTickRate:30 \ No newline at end of file From 57da4229ff621a12a43ae704bbd21884a9039d74 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Wed, 22 May 2024 13:58:59 +0200 Subject: [PATCH 529/581] Add speed value to Toast --- osu.Game/Localisation/ToastStrings.cs | 4 ++-- osu.Game/Overlays/OSD/SpeedChangeToast.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/ToastStrings.cs b/osu.Game/Localisation/ToastStrings.cs index 33027966dd..25899153f8 100644 --- a/osu.Game/Localisation/ToastStrings.cs +++ b/osu.Game/Localisation/ToastStrings.cs @@ -50,9 +50,9 @@ namespace osu.Game.Localisation public static LocalisableString UrlCopied => new TranslatableString(getKey(@"url_copied"), @"URL copied"); /// - /// "Speed Changed" + /// "Speed changed to" /// - public static LocalisableString SpeedChanged => new TranslatableString(getKey(@"speed_changed"), @"Speed Changed"); + public static LocalisableString SpeedChangedTo => new TranslatableString(getKey(@"speed_changed"), @"Speed changed to"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/OSD/SpeedChangeToast.cs b/osu.Game/Overlays/OSD/SpeedChangeToast.cs index 231ef86526..df4f825541 100644 --- a/osu.Game/Overlays/OSD/SpeedChangeToast.cs +++ b/osu.Game/Overlays/OSD/SpeedChangeToast.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 System.Threading; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Localisation; @@ -9,8 +10,8 @@ namespace osu.Game.Overlays.OSD { public partial class SpeedChangeToast : Toast { - public SpeedChangeToast(OsuConfigManager config, double delta) - : base(CommonStrings.Beatmaps, ToastStrings.SpeedChanged, config.LookupKeyBindings(GlobalAction.IncreaseModSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseModSpeed)) + public SpeedChangeToast(OsuConfigManager config, double newSpeed) + : base(CommonStrings.Beatmaps, ToastStrings.SpeedChangedTo + " " + newSpeed.ToString(Thread.CurrentThread.CurrentCulture), config.LookupKeyBindings(GlobalAction.IncreaseModSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseModSpeed)) { } } From abc67ebbaccfdc3d36ef1853f766829685e1308e Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Wed, 22 May 2024 13:59:26 +0200 Subject: [PATCH 530/581] Fix test not running due to floating point number inaccuacy --- .../SongSelect/TestScenePlaySongSelect.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 938b858110..af8b2a7760 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -94,25 +94,25 @@ namespace osu.Game.Tests.Visual.SongSelect changeMods(); AddStep("decreasing speed without mods", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.95); + AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); AddStep("decreasing speed with halftime", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.9", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.9); + AddAssert("halftime at 0.9", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.9); AddStep("increasing speed with halftime", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.95); + AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); AddStep("increasing speed with halftime to nomod", () => songSelect?.ChangeSpeed(+0.05)); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); AddStep("increasing speed without mods", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.05); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); AddStep("increasing speed with doubletime", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("doubletime at 1.1", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.1); + AddAssert("doubletime at 1.1", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.1); AddStep("decreasing speed with doubletime", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && mod.SpeedChange.Value == 1.05); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); OsuModNightcore nc = new OsuModNightcore { @@ -120,22 +120,22 @@ namespace osu.Game.Tests.Visual.SongSelect }; changeMods(nc); AddStep("increasing speed with nightcore", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("nightcore at 1.1", () => songSelect!.Mods.Value.Single() is ModNightcore mod && mod.SpeedChange.Value == 1.1); + AddAssert("nightcore at 1.1", () => songSelect!.Mods.Value.Single() is ModNightcore mod && Math.Round(mod.SpeedChange.Value, 2) == 1.1); AddStep("decreasing speed with nightcore", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModNightcore mod && mod.SpeedChange.Value == 1.05); + AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModNightcore mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); AddStep("decreasing speed with nightcore to nomod", () => songSelect?.ChangeSpeed(-0.05)); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); AddStep("decreasing speed nomod, nightcore was selected", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.95); + AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); AddStep("decreasing speed with daycore", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("daycore at 0.9", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.9); + AddAssert("daycore at 0.9", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.9); AddStep("increasing speed with daycore", () => songSelect?.ChangeSpeed(0.05)); - AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && mod.SpeedChange.Value == 0.95); + AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); OsuModDoubleTime dt = new OsuModDoubleTime { @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.SongSelect }; changeMods(dt); AddStep("decreasing speed from doubletime 1.02 with adjustpitch enabled", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.97 with adjustpitch enabled", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && mod.SpeedChange.Value == 0.97 && mod.AdjustPitch.Value); + AddAssert("halftime at 0.97 with adjustpitch enabled", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.97 && mod.AdjustPitch.Value); OsuModHalfTime ht = new OsuModHalfTime { @@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual.SongSelect Mod[] modlist = { ht, new OsuModHardRock(), new OsuModHidden() }; changeMods(modlist); AddStep("decreasing speed from halftime 0.97 with adjustpitch enabled, HDHR enabled", () => songSelect?.ChangeSpeed(0.05)); - AddAssert("doubletime at 1.02 with adjustpitch enabled, HDHR still enabled", () => songSelect!.Mods.Value.Count(mod => (mod is ModDoubleTime modDt && modDt.AdjustPitch.Value && modDt.SpeedChange.Value == 1.02) || mod is ModHardRock || mod is ModHidden) == 3); + AddAssert("doubletime at 1.02 with adjustpitch enabled, HDHR still enabled", () => songSelect!.Mods.Value.Count(mod => (mod is ModDoubleTime modDt && modDt.AdjustPitch.Value && Math.Round(modDt.SpeedChange.Value, 2) == 1.02) || mod is ModHardRock || mod is ModHidden) == 3); changeMods(new ModWindUp()); AddStep("windup active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); From 0df634574fc5b680e168d59966a2d537a2baa160 Mon Sep 17 00:00:00 2001 From: Fabian van Oeffelt Date: Wed, 22 May 2024 13:59:33 +0200 Subject: [PATCH 531/581] Improve readability --- osu.Game/Screens/Select/SongSelect.cs | 223 ++++++++++++++------------ 1 file changed, 119 insertions(+), 104 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 257f6583a4..b3823d7a0f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -823,123 +823,138 @@ namespace osu.Game.Screens.Select return false; } + private Mod getRateMod(ModType modType, Type type) + { + var modList = game.AvailableMods.Value[modType]; + var multiMod = (MultiMod)modList.First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(mod2 => mod2.GetType().IsSubclassOf(type)) > 0); + var mod = multiMod.Mods.First(mod => mod.GetType().IsSubclassOf(type)); + return mod; + } + public void ChangeSpeed(double delta) { - ModNightcore modNc = (ModNightcore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModNightcore) > 0)).Mods.First(mod => mod is ModNightcore); - ModDoubleTime modDt = (ModDoubleTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyIncrease].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDoubleTime) > 0)).Mods.First(mod => mod is ModDoubleTime); - ModDaycore modDc = (ModDaycore)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModDaycore) > 0)).Mods.First(mod => mod is ModDaycore); - ModHalfTime modHt = (ModHalfTime)((MultiMod)game.AvailableMods.Value[ModType.DifficultyReduction].First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(modType => modType is ModHalfTime) > 0)).Mods.First(mod => mod is ModHalfTime); + ModNightcore modNc = (ModNightcore)getRateMod(ModType.DifficultyIncrease, typeof(ModNightcore)); + ModDoubleTime modDt = (ModDoubleTime)getRateMod(ModType.DifficultyIncrease, typeof(ModDoubleTime)); + ModDaycore modDc = (ModDaycore)getRateMod(ModType.DifficultyReduction, typeof(ModDaycore)); + ModHalfTime modHt = (ModHalfTime)getRateMod(ModType.DifficultyReduction, typeof(ModHalfTime)); bool rateModActive = selectedMods.Value.Count(mod => mod is ModRateAdjust) > 0; bool incompatibleModActive = selectedMods.Value.Count(mod => modDt.IncompatibleMods.Count(incompatibleMod => (mod.GetType().IsSubclassOf(incompatibleMod) || mod.GetType() == incompatibleMod) && incompatibleMod != typeof(ModRateAdjust)) > 0) > 0; - double newRate = 1d + delta; + double newRate = Math.Round(1d + delta, 2); bool isPositive = delta > 0; if (incompatibleModActive) return; - onScreenDisplay?.Display(new SpeedChangeToast(config, delta)); - - if (rateModActive) + if (!rateModActive) { - ModRateAdjust mod = (ModRateAdjust)selectedMods.Value.First(mod => mod is ModRateAdjust); + onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); - // Find current active rateAdjust mod and modify speed, enable HalfTime if necessary - newRate = mod.SpeedChange.Value + delta; - - if (newRate == 1.0) - { - lastPitchState = false; - usedPitchMods = false; - - if (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) lastPitchState = true; - - if (mod is ModHalfTime htmod && htmod.AdjustPitch.Value) lastPitchState = true; - - if (mod is ModNightcore || mod is ModDaycore) usedPitchMods = true; - - //Disable RateAdjustMods - selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); - return; - } - - if (((mod is ModDoubleTime || mod is ModNightcore) && newRate < mod.SpeedChange.MinValue) - || ((mod is ModHalfTime || mod is ModDaycore) && newRate > mod.SpeedChange.MaxValue)) - { - bool adjustPitch = (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) || (mod is ModHalfTime htmod && htmod.AdjustPitch.Value); - - //Disable RateAdjustMods - selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); - - ModRateAdjust? oppositeMod = null; - - switch (mod) - { - case ModDoubleTime: - modHt.AdjustPitch.Value = adjustPitch; - oppositeMod = modHt; - break; - - case ModHalfTime: - modDt.AdjustPitch.Value = adjustPitch; - oppositeMod = modDt; - break; - - case ModNightcore: - oppositeMod = modDc; - break; - - case ModDaycore: - oppositeMod = modNc; - break; - } - - if (oppositeMod == null) return; - - oppositeMod.SpeedChange.Value = newRate; - selectedMods.Value = selectedMods.Value.Append(oppositeMod).ToList(); - return; - } - - if (newRate > mod.SpeedChange.MaxValue && (mod is ModDoubleTime || mod is ModNightcore)) - newRate = mod.SpeedChange.MaxValue; - - if (newRate < mod.SpeedChange.MinValue && (mod is ModHalfTime || mod is ModDaycore)) - newRate = mod.SpeedChange.MinValue; - - mod.SpeedChange.Value = newRate; - } - else - { // If no ModRateAdjust is active, activate one - if (isPositive) - { - if (!usedPitchMods) - { - modDt.SpeedChange.Value = newRate; - modDt.AdjustPitch.Value = lastPitchState; - selectedMods.Value = selectedMods.Value.Append(modDt).ToList(); - } - else - { - modNc.SpeedChange.Value = newRate; - selectedMods.Value = selectedMods.Value.Append(modNc).ToList(); - } - } - else - { - if (!usedPitchMods) - { - modHt.SpeedChange.Value = newRate; - modHt.AdjustPitch.Value = lastPitchState; - selectedMods.Value = selectedMods.Value.Append(modHt).ToList(); - } - else - { - modDc.SpeedChange.Value = newRate; - selectedMods.Value = selectedMods.Value.Append(modDc).ToList(); - } - } + ModRateAdjust? newMod = null; + + if (isPositive && !usedPitchMods) + newMod = modDt; + + if (isPositive && usedPitchMods) + newMod = modNc; + + if (!isPositive && !usedPitchMods) + newMod = modHt; + + if (!isPositive && usedPitchMods) + newMod = modDc; + + if (!usedPitchMods && newMod is ModDoubleTime newModDt) + newModDt.AdjustPitch.Value = lastPitchState; + + if (!usedPitchMods && newMod is ModHalfTime newModHt) + newModHt.AdjustPitch.Value = lastPitchState; + + newMod!.SpeedChange.Value = newRate; + selectedMods.Value = selectedMods.Value.Append(newMod).ToList(); + return; } + + ModRateAdjust mod = (ModRateAdjust)selectedMods.Value.First(mod => mod is ModRateAdjust); + newRate = Math.Round(mod.SpeedChange.Value + delta, 2); + + // Disable RateAdjustMods if newRate is 1 + if (newRate == 1.0) + { + lastPitchState = false; + usedPitchMods = false; + + if (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) + lastPitchState = true; + + if (mod is ModHalfTime htmod && htmod.AdjustPitch.Value) + lastPitchState = true; + + if (mod is ModNightcore || mod is ModDaycore) + usedPitchMods = true; + + //Disable RateAdjustMods + selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); + + onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); + + return; + } + + bool overMaxRateLimit = (mod is ModHalfTime || mod is ModDaycore) && newRate > mod.SpeedChange.MaxValue; + bool underMinRateLimit = (mod is ModDoubleTime || mod is ModNightcore) && newRate < mod.SpeedChange.MinValue; + + // Swap mod to opposite mod if newRate exceeds max/min speed values + if (overMaxRateLimit || underMinRateLimit) + { + bool adjustPitch = (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) || (mod is ModHalfTime htmod && htmod.AdjustPitch.Value); + + //Disable RateAdjustMods + selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); + + ModRateAdjust? oppositeMod = null; + + switch (mod) + { + case ModDoubleTime: + modHt.AdjustPitch.Value = adjustPitch; + oppositeMod = modHt; + break; + + case ModHalfTime: + modDt.AdjustPitch.Value = adjustPitch; + oppositeMod = modDt; + break; + + case ModNightcore: + oppositeMod = modDc; + break; + + case ModDaycore: + oppositeMod = modNc; + break; + } + + if (oppositeMod == null) return; + + oppositeMod.SpeedChange.Value = newRate; + selectedMods.Value = selectedMods.Value.Append(oppositeMod).ToList(); + + onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); + + return; + } + + // Cap newRate to max/min values and change rate of current active mod + if (newRate > mod.SpeedChange.MaxValue && (mod is ModDoubleTime || mod is ModNightcore)) + newRate = mod.SpeedChange.MaxValue; + + if (newRate < mod.SpeedChange.MinValue && (mod is ModHalfTime || mod is ModDaycore)) + newRate = mod.SpeedChange.MinValue; + + mod.SpeedChange.Value = newRate; + + onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); } protected override void Dispose(bool isDisposing) From 8d02ac5e219ad064e553c560d076224683ad2651 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 21:20:34 +0800 Subject: [PATCH 532/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e20ac2e0b7..8fefce3a60 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 103ef50e0c..29a0350fde 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 66ceda1d674ce57395419b9157daec91128d403f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 21:27:53 +0800 Subject: [PATCH 533/581] Update focus specifications in line with framework changes --- .../Screens/Ladder/Components/LadderEditorSettings.cs | 2 +- osu.Game/Collections/ManageCollectionsDialog.cs | 2 +- osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs | 2 +- osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs | 2 +- osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 2 +- osu.Game/Overlays/Comments/ReplyCommentEditor.cs | 2 +- osu.Game/Overlays/Login/LoginForm.cs | 2 +- osu.Game/Overlays/Login/LoginPanel.cs | 4 ++-- osu.Game/Overlays/Login/SecondFactorAuthForm.cs | 2 +- osu.Game/Overlays/LoginOverlay.cs | 2 +- osu.Game/Overlays/Mods/AddPresetPopover.cs | 2 +- osu.Game/Overlays/Mods/EditPresetPopover.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs | 2 +- .../Settings/Sections/Input/KeyBindingsSubsection.cs | 2 +- osu.Game/Overlays/SettingsPanel.cs | 2 +- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 2 +- .../Compose/Components/Timeline/DifficultyPointPiece.cs | 2 +- .../Edit/Compose/Components/Timeline/SamplePointPiece.cs | 2 +- osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs | 2 +- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 2 +- .../Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 2 +- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 6 +++--- osu.Game/Screens/Select/FilterControl.cs | 2 +- osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs | 2 +- 26 files changed, 29 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 9f0fa19915..08ed815253 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.ValueChanged += selection => { // ensure any ongoing edits are committed out to the *current* selection before changing to a new one. - GetContainingInputManager().TriggerFocusContention(null); + GetContainingFocusManager().TriggerFocusContention(null); // Required to avoid cyclic failure in BindableWithCurrent (TriggerChange called during the Current_Set process). // Arguable a framework issue but since we haven't hit it anywhere else a local workaround seems best. diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs index 16645d6796..ea663f45fe 100644 --- a/osu.Game/Collections/ManageCollectionsDialog.cs +++ b/osu.Game/Collections/ManageCollectionsDialog.cs @@ -137,7 +137,7 @@ namespace osu.Game.Collections this.ScaleTo(0.9f, exit_duration); // Ensure that textboxes commit - GetContainingInputManager()?.TriggerFocusContention(this); + GetContainingFocusManager()?.TriggerFocusContention(this); } } } diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 338f32f321..4ec93995a4 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface if (!allowImmediateFocus) return; - Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this)); + Scheduler.Add(() => GetContainingFocusManager().ChangeFocus(this)); } public new void KillFocus() => base.KillFocus(); diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index 8b9d35e343..863ad5a173 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -57,7 +57,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override void OnFocus(FocusEvent e) { base.OnFocus(e); - GetContainingInputManager().ChangeFocus(Component); + GetContainingFocusManager().ChangeFocus(Component); } protected override OsuTextBox CreateComponent() => CreateTextBox().With(t => diff --git a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index abd828e98f..4c16cb4951 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -85,7 +85,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Current.BindValueChanged(updateTextBoxFromSlider, true); } - public bool TakeFocus() => GetContainingInputManager().ChangeFocus(textBox); + public bool TakeFocus() => GetContainingFocusManager().ChangeFocus(textBox); private bool updatingFromTextBox; diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index f57c7d22a2..53e51e0611 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -243,7 +243,7 @@ namespace osu.Game.Overlays.AccountCreation if (nextTextBox != null) { - Schedule(() => GetContainingInputManager().ChangeFocus(nextTextBox)); + Schedule(() => GetContainingFocusManager().ChangeFocus(nextTextBox)); return true; } diff --git a/osu.Game/Overlays/Comments/ReplyCommentEditor.cs b/osu.Game/Overlays/Comments/ReplyCommentEditor.cs index 8e9e82507d..caf19829ee 100644 --- a/osu.Game/Overlays/Comments/ReplyCommentEditor.cs +++ b/osu.Game/Overlays/Comments/ReplyCommentEditor.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Comments base.LoadComplete(); if (!TextBox.ReadOnly) - GetContainingInputManager().ChangeFocus(TextBox); + GetContainingFocusManager().ChangeFocus(TextBox); } protected override void OnCommit(string text) diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index 80dfca93d2..418721f371 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Login protected override void OnFocus(FocusEvent e) { - Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); + Schedule(() => { GetContainingFocusManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); } } } diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index a8adf4ce8c..845d20ccaf 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -186,7 +186,7 @@ namespace osu.Game.Overlays.Login } if (form != null) - ScheduleAfterChildren(() => GetContainingInputManager()?.ChangeFocus(form)); + ScheduleAfterChildren(() => GetContainingFocusManager()?.ChangeFocus(form)); }); private void updateDropdownCurrent(UserStatus? status) @@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Login protected override void OnFocus(FocusEvent e) { - if (form != null) GetContainingInputManager().ChangeFocus(form); + if (form != null) GetContainingFocusManager().ChangeFocus(form); base.OnFocus(e); } } diff --git a/osu.Game/Overlays/Login/SecondFactorAuthForm.cs b/osu.Game/Overlays/Login/SecondFactorAuthForm.cs index dcd3119f33..82e328c036 100644 --- a/osu.Game/Overlays/Login/SecondFactorAuthForm.cs +++ b/osu.Game/Overlays/Login/SecondFactorAuthForm.cs @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Login protected override void OnFocus(FocusEvent e) { - Schedule(() => { GetContainingInputManager().ChangeFocus(codeTextBox); }); + Schedule(() => { GetContainingFocusManager().ChangeFocus(codeTextBox); }); } } } diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index c0aff6aae9..8dc454c0a0 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -78,7 +78,7 @@ namespace osu.Game.Overlays this.FadeIn(transition_time, Easing.OutQuint); FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(panel)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(panel)); } protected override void PopOut() diff --git a/osu.Game/Overlays/Mods/AddPresetPopover.cs b/osu.Game/Overlays/Mods/AddPresetPopover.cs index b782b5d6ba..50aa5a2eb4 100644 --- a/osu.Game/Overlays/Mods/AddPresetPopover.cs +++ b/osu.Game/Overlays/Mods/AddPresetPopover.cs @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(nameTextBox)); nameTextBox.Current.BindValueChanged(s => { diff --git a/osu.Game/Overlays/Mods/EditPresetPopover.cs b/osu.Game/Overlays/Mods/EditPresetPopover.cs index 9554ba8ce2..8fa6b35162 100644 --- a/osu.Game/Overlays/Mods/EditPresetPopover.cs +++ b/osu.Game/Overlays/Mods/EditPresetPopover.cs @@ -136,7 +136,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(nameTextBox)); } public override bool OnPressed(KeyBindingPressEvent e) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 25293e8e20..54124e10c7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -949,7 +949,7 @@ namespace osu.Game.Overlays.Mods RequestScroll?.Invoke(this); // Killing focus is done here because it's the only feasible place on ModSelectOverlay you can click on without triggering any action. - Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null)); + Scheduler.Add(() => GetContainingFocusManager().ChangeFocus(null)); return true; } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index e82cebe9f4..3f6eeca10e 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -465,7 +465,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input } if (HasFocus) - GetContainingInputManager().ChangeFocus(null); + GetContainingFocusManager().ChangeFocus(null); cancelAndClearButtons.FadeOut(300, Easing.OutQuint); cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index dd0a88bfb1..db3b56b9f0 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -106,7 +106,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { var next = Children.SkipWhile(c => c != sender).Skip(1).FirstOrDefault(); if (next != null) - GetContainingInputManager().ChangeFocus(next); + GetContainingFocusManager().ChangeFocus(next); } } } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 748673035b..d5c642d24f 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -201,7 +201,7 @@ namespace osu.Game.Overlays searchTextBox.HoldFocus = false; if (searchTextBox.HasFocus) - GetContainingInputManager().ChangeFocus(null); + GetContainingFocusManager().ChangeFocus(null); } public override bool AcceptsFocus => true; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 40b97d2137..005b96bfef 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -580,7 +580,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - GetContainingInputManager().ChangeFocus(this); + GetContainingFocusManager().ChangeFocus(this); SelectAll(); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index fc240c570b..d9084a7477 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -138,7 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override void LoadComplete() { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(sliderVelocitySlider)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(sliderVelocitySlider)); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index 28841fc9e5..5c4a9faaca 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -142,7 +142,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override void LoadComplete() { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(volume)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(volume)); } private static string? getCommonBank(IList[] relevantSamples) => relevantSamples.Select(GetBankValue).Distinct().Count() == 1 ? GetBankValue(relevantSamples.First()) : null; diff --git a/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs index 79288e2977..5abf40dda7 100644 --- a/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs +++ b/osu.Game/Screens/Edit/Setup/LabelledTextBoxWithPopover.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Setup OnFocused?.Invoke(); base.OnFocus(e); - GetContainingInputManager().TriggerFocusContention(this); + GetContainingFocusManager().TriggerFocusContention(this); } } } diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 752f590308..660c470204 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Setup base.LoadComplete(); if (string.IsNullOrEmpty(ArtistTextBox.Current.Value)) - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(ArtistTextBox)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(ArtistTextBox)); ArtistTextBox.Current.BindValueChanged(artist => transferIfRomanised(artist.NewValue, RomanisedArtistTextBox)); TitleTextBox.Current.BindValueChanged(title => transferIfRomanised(title.NewValue, RomanisedTitleTextBox)); diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index 26f374ba85..4f7a1bf589 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Edit.Timing protected override void OnFocus(FocusEvent e) { base.OnFocus(e); - GetContainingInputManager().ChangeFocus(textBox); + GetContainingFocusManager().ChangeFocus(textBox); } private void updateState() diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 66bbf92e58..2f6a220c82 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -248,21 +248,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(passwordTextBox)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(passwordTextBox)); passwordTextBox.OnCommit += (_, _) => performJoin(); } private void performJoin() { lounge?.Join(room, passwordTextBox.Text, null, joinFailed); - GetContainingInputManager().TriggerFocusContention(passwordTextBox); + GetContainingFocusManager().TriggerFocusContention(passwordTextBox); } private void joinFailed(string error) => Schedule(() => { passwordTextBox.Text = string.Empty; - GetContainingInputManager().ChangeFocus(passwordTextBox); + GetContainingFocusManager().ChangeFocus(passwordTextBox); errorText.Text = error; errorText diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 73c122dda6..30eb4a8491 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -245,7 +245,7 @@ namespace osu.Game.Screens.Select searchTextBox.ReadOnly = true; searchTextBox.HoldFocus = false; if (searchTextBox.HasFocus) - GetContainingInputManager().ChangeFocus(searchTextBox); + GetContainingFocusManager().ChangeFocus(searchTextBox); } public void Activate() diff --git a/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs b/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs index f73be15a36..2827a9cb50 100644 --- a/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs +++ b/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.SelectV2.Footer { base.LoadComplete(); - ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(this)); + ScheduleAfterChildren(() => GetContainingFocusManager().ChangeFocus(this)); beatmap.BindValueChanged(_ => Hide()); } From f7ca18b52ec4e8dbc704d58a7cfef0c301201524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 22 May 2024 15:52:57 +0200 Subject: [PATCH 534/581] Menial cleanups --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index e5567b2215..8ea1d55a0d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -105,18 +105,18 @@ namespace osu.Game.Beatmaps.Formats } /// - /// Clamp Difficulty settings to be within the normal range. + /// Ensures that all settings are within the allowed ranges. + /// See also: https://github.com/peppy/osu-stable-reference/blob/0e425c0d525ef21353c8293c235cc0621d28338b/osu!/GameplayElements/Beatmaps/Beatmap.cs#L567-L614 /// private void applyDifficultyRestrictions(BeatmapDifficulty difficulty) { difficulty.DrainRate = Math.Clamp(difficulty.DrainRate, 0, 10); - //If the mode is not Mania, clamp circle size to [0,10] - if (beatmap.BeatmapInfo.Ruleset.OnlineID != 3) - difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 0, 10); - //If it is Mania, it must be within [1,18] - copying what stable does https://github.com/ppy/osu/pull/28200#discussion_r1609522988 - //The lower bound should be 4, but there are ranked maps that are lower than this. - else - difficulty.CircleSize = Math.Clamp(difficulty.CircleSize, 1, 18); + + // mania uses "circle size" for key count, thus different allowable range + difficulty.CircleSize = beatmap.BeatmapInfo.Ruleset.OnlineID != 3 + ? Math.Clamp(difficulty.CircleSize, 0, 10) + : Math.Clamp(difficulty.CircleSize, 1, 18); + difficulty.OverallDifficulty = Math.Clamp(difficulty.OverallDifficulty, 0, 10); difficulty.ApproachRate = Math.Clamp(difficulty.ApproachRate, 0, 10); From 093be3d723ef18bcb3e7eaac1642c0006b62f922 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 21:55:53 +0800 Subject: [PATCH 535/581] Cast remaining test usages to `IFocusManager` to remove obsolete notice --- .../TestSceneHitObjectSampleAdjustments.cs | 3 ++- .../Editing/TestSceneLabelledTimeSignature.cs | 9 +++++---- .../UserInterface/TestSceneModSelectOverlay.cs | 3 ++- .../TestSceneSliderWithTextBoxInput.cs | 17 +++++++++-------- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 1415ff4b0f..0e12ed68e4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Collections.Generic; using Humanizer; using NUnit.Framework; +using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -396,7 +397,7 @@ namespace osu.Game.Tests.Visual.Editing textBox.Current.Value = bank; // force a commit via keyboard. // this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit. - InputManager.ChangeFocus(textBox); + ((IFocusManager)InputManager).ChangeFocus(textBox); InputManager.Key(Key.Enter); }); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneLabelledTimeSignature.cs b/osu.Game.Tests/Visual/Editing/TestSceneLabelledTimeSignature.cs index e91596b872..3d7d0797d4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneLabelledTimeSignature.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneLabelledTimeSignature.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics.UserInterface; @@ -62,12 +63,12 @@ namespace osu.Game.Tests.Visual.Editing createLabelledTimeSignature(TimeSignature.SimpleQuadruple); AddAssert("current is 4/4", () => timeSignature.Current.Value.Equals(TimeSignature.SimpleQuadruple)); - AddStep("focus text box", () => InputManager.ChangeFocus(numeratorTextBox)); + AddStep("focus text box", () => ((IFocusManager)InputManager).ChangeFocus(numeratorTextBox)); AddStep("set numerator to 7", () => numeratorTextBox.Current.Value = "7"); AddAssert("current is 4/4", () => timeSignature.Current.Value.Equals(TimeSignature.SimpleQuadruple)); - AddStep("drop focus", () => InputManager.ChangeFocus(null)); + AddStep("drop focus", () => ((IFocusManager)InputManager).ChangeFocus(null)); AddAssert("current is 7/4", () => timeSignature.Current.Value.Equals(new TimeSignature(7))); } @@ -77,12 +78,12 @@ namespace osu.Game.Tests.Visual.Editing createLabelledTimeSignature(TimeSignature.SimpleQuadruple); AddAssert("current is 4/4", () => timeSignature.Current.Value.Equals(TimeSignature.SimpleQuadruple)); - AddStep("focus text box", () => InputManager.ChangeFocus(numeratorTextBox)); + AddStep("focus text box", () => ((IFocusManager)InputManager).ChangeFocus(numeratorTextBox)); AddStep("set numerator to 0", () => numeratorTextBox.Current.Value = "0"); AddAssert("current is 4/4", () => timeSignature.Current.Value.Equals(TimeSignature.SimpleQuadruple)); - AddStep("drop focus", () => InputManager.ChangeFocus(null)); + AddStep("drop focus", () => ((IFocusManager)InputManager).ChangeFocus(null)); AddAssert("current is 4/4", () => timeSignature.Current.Value.Equals(TimeSignature.SimpleQuadruple)); AddAssert("numerator is 4", () => numeratorTextBox.Current.Value == "4"); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 8ddbd84890..a1452ddb31 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Framework.Utils; @@ -623,7 +624,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("press tab", () => InputManager.Key(Key.Tab)); AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); - AddStep("unfocus search text box externally", () => InputManager.ChangeFocus(null)); + AddStep("unfocus search text box externally", () => ((IFocusManager)InputManager).ChangeFocus(null)); AddStep("press tab", () => InputManager.Key(Key.Tab)); AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs index d23fcebae3..06b9623508 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSliderWithTextBoxInput.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -42,7 +43,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("set instantaneous to false", () => sliderWithTextBoxInput.Instantaneous = false); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("change text", () => textBox.Text = "3"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.Zero); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.Zero); @@ -61,7 +62,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("set text to invalid", () => textBox.Text = "garbage"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); @@ -71,12 +72,12 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("set text to invalid", () => textBox.Text = "garbage"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("lose focus", () => InputManager.ChangeFocus(null)); + AddStep("lose focus", () => ((IFocusManager)InputManager).ChangeFocus(null)); AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); @@ -87,7 +88,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("set instantaneous to true", () => sliderWithTextBoxInput.Instantaneous = true); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("change text", () => textBox.Text = "3"); AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3)); AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3)); @@ -106,7 +107,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("-5")); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("set text to invalid", () => textBox.Text = "garbage"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); @@ -116,12 +117,12 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("focus textbox", () => InputManager.ChangeFocus(textBox)); + AddStep("focus textbox", () => ((IFocusManager)InputManager).ChangeFocus(textBox)); AddStep("set text to invalid", () => textBox.Text = "garbage"); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); - AddStep("lose focus", () => InputManager.ChangeFocus(null)); + AddStep("lose focus", () => ((IFocusManager)InputManager).ChangeFocus(null)); AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5")); AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5)); AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5)); From 0d13848421de5198fc438fd7f4a2420c265dc31b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 23 May 2024 00:21:19 +0900 Subject: [PATCH 536/581] Add whitespace to appease R# --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index e6daba2016..a4cd888823 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1206,6 +1206,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(decoded.SliderTickRate, Is.EqualTo(8)); } } + [Test] public void TestManiaBeatmapDifficultyCircleSizeClamp() { From 73cb363eba003fd329477ab778f28610d0b9e596 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 May 2024 23:25:59 +0800 Subject: [PATCH 537/581] Make some more methods static --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 8ea1d55a0d..c2f4097889 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -85,7 +85,7 @@ namespace osu.Game.Beatmaps.Formats base.ParseStreamInto(stream, beatmap); - applyDifficultyRestrictions(beatmap.Difficulty); + applyDifficultyRestrictions(beatmap.Difficulty, beatmap); flushPendingPoints(); @@ -108,7 +108,7 @@ namespace osu.Game.Beatmaps.Formats /// Ensures that all settings are within the allowed ranges. /// See also: https://github.com/peppy/osu-stable-reference/blob/0e425c0d525ef21353c8293c235cc0621d28338b/osu!/GameplayElements/Beatmaps/Beatmap.cs#L567-L614 /// - private void applyDifficultyRestrictions(BeatmapDifficulty difficulty) + private static void applyDifficultyRestrictions(BeatmapDifficulty difficulty, Beatmap beatmap) { difficulty.DrainRate = Math.Clamp(difficulty.DrainRate, 0, 10); @@ -127,7 +127,7 @@ namespace osu.Game.Beatmaps.Formats /// /// Processes the beatmap such that a new combo is started the first hitobject following each break. /// - private void postProcessBreaks(Beatmap beatmap) + private static void postProcessBreaks(Beatmap beatmap) { int currentBreak = 0; bool forceNewCombo = false; @@ -183,7 +183,7 @@ namespace osu.Game.Beatmaps.Formats /// This method's intention is to restore those legacy defaults. /// See also: https://osu.ppy.sh/wiki/en/Client/File_formats/Osu_%28file_format%29 /// - private void applyLegacyDefaults(BeatmapInfo beatmapInfo) + private static void applyLegacyDefaults(BeatmapInfo beatmapInfo) { beatmapInfo.WidescreenStoryboard = false; beatmapInfo.SamplesMatchPlaybackRate = false; From c3a2a1361d045dbfd7c409fcd003ddc33dd54164 Mon Sep 17 00:00:00 2001 From: Aurelian Date: Wed, 22 May 2024 10:39:42 +0200 Subject: [PATCH 538/581] SliderBody's Size getter updates size to the body/path's Size --- .../Sliders/Components/SliderBodyPiece.cs | 12 +++++++++++- .../Skinning/Default/ManualSliderBody.cs | 13 ++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 075e9e6aa1..14d72a2d36 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -61,10 +61,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components body.SetVertices(vertices); } - Size = body.Size; OriginPosition = body.PathOffset; } + public override Vector2 Size + { + get + { + if (base.Size != body.Size) + Size = body.Size; + return base.Size; + } + set => base.Size = value; + } + public void RecyclePath() => body.RecyclePath(); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs index d171f56f40..99d954059c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs @@ -11,10 +11,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default /// public partial class ManualSliderBody : SliderBody { - public new void SetVertices(IReadOnlyList vertices) + public new void SetVertices(IReadOnlyList vertices) => base.SetVertices(vertices); + + public override Vector2 Size { - base.SetVertices(vertices); - Size = Path.Size; + get + { + if (base.Size != Path.Size) + Size = Path.Size; + return base.Size; + } + set => base.Size = value; } } } From fd9f8bd3e098ed85b84c562afd884e1191018d25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 01:20:58 +0800 Subject: [PATCH 539/581] Update framework --- osu.Android.props | 2 +- osu.Game/Collections/CollectionDropdown.cs | 7 ++++++- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8fefce3a60..1f241c6db5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 29a0350fde..eba9abd3b8 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From f85a1339d9e8b0a185aa48d38912ba971556bd73 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 14:12:27 +0800 Subject: [PATCH 540/581] Unload daily challenge background less aggressively --- .../UpdateableOnlineBeatmapSetCover.cs | 17 +++++++++++------ osu.Game/Screens/Menu/DailyChallengeButton.cs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs index 2a6b6f90e3..5bce472613 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs @@ -27,8 +27,17 @@ namespace osu.Game.Beatmaps.Drawables set => base.Masking = value; } - public UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover) + protected override double LoadDelay { get; } + + private readonly double timeBeforeUnload; + + protected override double TransformDuration => 400; + + public UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType coverType = BeatmapSetCoverType.Cover, double timeBeforeLoad = 500, double timeBeforeUnload = 1000) { + LoadDelay = timeBeforeLoad; + this.timeBeforeUnload = timeBeforeUnload; + this.coverType = coverType; InternalChild = new Box @@ -38,12 +47,8 @@ namespace osu.Game.Beatmaps.Drawables }; } - protected override double LoadDelay => 500; - - protected override double TransformDuration => 400; - protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) - => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad) + => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, timeBeforeUnload) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 907fd04148..28b3747fbf 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Menu { Children = new Drawable[] { - cover = new UpdateableOnlineBeatmapSetCover + cover = new UpdateableOnlineBeatmapSetCover(timeBeforeLoad: 0, timeBeforeUnload: 600_000) { RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, From 84fe3699f641b3489eda5730a2f94ea2317e53ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 14:31:20 +0800 Subject: [PATCH 541/581] Reorder test steps to work better on multiple runs --- .../UserInterface/TestSceneMainMenuButton.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs index 921e28d607..5914898cb1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs @@ -39,12 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDailyChallengeButton() { - AddStep("add button", () => Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - ButtonSystemState = ButtonSystemState.TopLevel, - }); + AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null)); AddStep("set up API", () => dummyAPI.HandleRequest = req => { @@ -72,12 +67,17 @@ namespace osu.Game.Tests.Visual.UserInterface } }); + AddStep("add button", () => Child = new DailyChallengeButton(@"button-default-select", new Color4(102, 68, 204, 255), _ => { }, 0, Key.D) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + ButtonSystemState = ButtonSystemState.TopLevel, + }); + AddStep("beatmap of the day active", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = 1234, })); - - AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null)); } } } From 88a2f74326183605a7130c3a74cdd09ebb6a9b36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 16:00:23 +0800 Subject: [PATCH 542/581] Adjust animation --- osu.Game/Screens/Menu/DailyChallengeButton.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 28b3747fbf..3e514d0c1f 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Graphics; @@ -72,8 +73,7 @@ namespace osu.Game.Screens.Menu RelativeSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativePositionAxes = Axes.X, - X = -0.5f, + RelativePositionAxes = Axes.Both, }, new Box { @@ -100,18 +100,25 @@ namespace osu.Game.Screens.Menu base.LoadComplete(); info.BindValueChanged(updateDisplay, true); - FinishTransforms(true); - - cover.MoveToX(-0.5f, 10000, Easing.InOutSine) - .Then().MoveToX(0.5f, 10000, Easing.InOutSine) - .Loop(); } protected override void Update() { base.Update(); - cover.Width = 2 * background.DrawWidth; + if (cover.LatestTransformEndTime == Time.Current) + { + const double duration = 3000; + + float scale = 1 + RNG.NextSingle(); + + cover.ScaleTo(scale, duration, Easing.InOutSine) + .RotateTo(RNG.NextSingle(-4, 4) * (scale - 1), duration, Easing.InOutSine) + .MoveTo(new Vector2( + RNG.NextSingle(-0.5f, 0.5f) * (scale - 1), + RNG.NextSingle(-0.5f, 0.5f) * (scale - 1) + ), duration, Easing.InOutSine); + } } private void updateDisplay(ValueChangedEvent info) From a3639e0ce3c8cec679432624f571c71443c84978 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 17:19:11 +0800 Subject: [PATCH 543/581] Remove unused field --- osu.Game/Screens/Menu/DailyChallengeButton.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 3e514d0c1f..7dbd90eeba 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -38,7 +38,6 @@ namespace osu.Game.Screens.Menu private UpdateableOnlineBeatmapSetCover cover = null!; private IBindable info = null!; - private BufferedContainer background = null!; [Resolved] private IAPIProvider api { get; set; } = null!; @@ -64,7 +63,7 @@ namespace osu.Game.Screens.Menu }); } - protected override Drawable CreateBackground(Colour4 accentColour) => background = new BufferedContainer + protected override Drawable CreateBackground(Colour4 accentColour) => new BufferedContainer { Children = new Drawable[] { From 357e55ae1f9d4d19fc7e28f508a73d2ca05e8751 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 May 2024 17:39:59 +0800 Subject: [PATCH 544/581] Make gradient layer a bit more dynamic --- osu.Game/Screens/Menu/DailyChallengeButton.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 7dbd90eeba..c365994736 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.Menu private UpdateableOnlineBeatmapSetCover cover = null!; private IBindable info = null!; + private Box gradientLayer = null!; + [Resolved] private IAPIProvider api { get; set; } = null!; @@ -74,10 +76,10 @@ namespace osu.Game.Screens.Menu Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, }, - new Box + gradientLayer = new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(accentColour.Opacity(0), accentColour), + Colour = ColourInfo.GradientVertical(accentColour.Opacity(0.2f), accentColour), Blending = BlendingParameters.Additive, }, new Box @@ -117,6 +119,10 @@ namespace osu.Game.Screens.Menu RNG.NextSingle(-0.5f, 0.5f) * (scale - 1), RNG.NextSingle(-0.5f, 0.5f) * (scale - 1) ), duration, Easing.InOutSine); + + gradientLayer.FadeIn(duration / 2) + .Then() + .FadeOut(duration / 2); } } From bfa23ec7a47ed41be7ffc40e46b28fd5fff2b648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 11:46:48 +0200 Subject: [PATCH 545/581] Fix main menu button animation not playing on initial show --- osu.Game/Screens/Menu/MainMenuButton.cs | 69 ++++++++++++++----------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index fe8fb91766..29a661066c 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -179,7 +179,6 @@ namespace osu.Game.Screens.Menu { base.LoadComplete(); - background.Size = initialSize; background.Shear = new Vector2(ButtonSystem.WEDGE_WIDTH / initialSize.Y, 0); // for whatever reason, attempting to size the background "just in time" to cover the visible width @@ -189,6 +188,9 @@ namespace osu.Game.Screens.Menu // (which can exceed the [0;1] range during interpolation). backgroundContent.Width = 2 * initialSize.X; backgroundContent.Shear = -background.Shear; + + animateState(); + FinishTransforms(true); } private bool rightward; @@ -318,41 +320,46 @@ namespace osu.Game.Screens.Menu state = value; - switch (state) - { - case ButtonState.Contracted: - switch (ContractStyle) - { - default: - background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 500, Easing.OutExpo); - this.FadeOut(500); - break; - - case 1: - background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 400, Easing.InSine); - this.FadeOut(800); - break; - } - - break; - - case ButtonState.Expanded: - const int expand_duration = 500; - background.ResizeTo(initialSize, expand_duration, Easing.OutExpo); - this.FadeIn(expand_duration / 6f); - break; - - case ButtonState.Exploded: - const int explode_duration = 200; - background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(2, 1)), explode_duration, Easing.OutExpo); - this.FadeOut(explode_duration / 4f * 3); - break; - } + animateState(); StateChanged?.Invoke(State); } } + private void animateState() + { + switch (state) + { + case ButtonState.Contracted: + switch (ContractStyle) + { + default: + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 500, Easing.OutExpo); + this.FadeOut(500); + break; + + case 1: + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(0, 1)), 400, Easing.InSine); + this.FadeOut(800); + break; + } + + break; + + case ButtonState.Expanded: + const int expand_duration = 500; + background.ResizeTo(initialSize, expand_duration, Easing.OutExpo); + this.FadeIn(expand_duration / 6f); + break; + + case ButtonState.Exploded: + const int explode_duration = 200; + background.ResizeTo(Vector2.Multiply(initialSize, new Vector2(2, 1)), explode_duration, Easing.OutExpo); + this.FadeOut(explode_duration / 4f * 3); + break; + } + } + private ButtonSystemState buttonSystemState; public ButtonSystemState ButtonSystemState From 3411ebc4af5711c275e21b843b264bc7d96f864b Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 23 May 2024 12:50:06 +0200 Subject: [PATCH 546/581] Move `SDL3BatteryInfo` to separate file --- osu.Desktop/OsuGameDesktop.cs | 20 -------------------- osu.Desktop/SDL3BatteryInfo.cs | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 osu.Desktop/SDL3BatteryInfo.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e8783c997a..b1e1a8f118 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,7 +22,6 @@ using osu.Game.IPC; using osu.Game.Online.Multiplayer; using osu.Game.Performance; using osu.Game.Utils; -using SDL; namespace osu.Desktop { @@ -169,24 +168,5 @@ namespace osu.Desktop osuSchemeLinkIPCChannel?.Dispose(); archiveImportIPCChannel?.Dispose(); } - - private unsafe class SDL3BatteryInfo : BatteryInfo - { - public override double? ChargeLevel - { - get - { - int percentage; - SDL3.SDL_GetPowerInfo(null, &percentage); - - if (percentage == -1) - return null; - - return percentage / 100.0; - } - } - - public override bool OnBattery => SDL3.SDL_GetPowerInfo(null, null) == SDL_PowerState.SDL_POWERSTATE_ON_BATTERY; - } } } diff --git a/osu.Desktop/SDL3BatteryInfo.cs b/osu.Desktop/SDL3BatteryInfo.cs new file mode 100644 index 0000000000..89084b5a15 --- /dev/null +++ b/osu.Desktop/SDL3BatteryInfo.cs @@ -0,0 +1,27 @@ +// 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.Utils; +using SDL; + +namespace osu.Desktop +{ + internal unsafe class SDL3BatteryInfo : BatteryInfo + { + public override double? ChargeLevel + { + get + { + int percentage; + SDL3.SDL_GetPowerInfo(null, &percentage); + + if (percentage == -1) + return null; + + return percentage / 100.0; + } + } + + public override bool OnBattery => SDL3.SDL_GetPowerInfo(null, null) == SDL_PowerState.SDL_POWERSTATE_ON_BATTERY; + } +} From 45ed86f46cdf413d1acaa189f0faea7a10b0ad44 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 23 May 2024 12:53:33 +0200 Subject: [PATCH 547/581] Add back `SDL2BatteryInfo` --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Desktop/SDL2BatteryInfo.cs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 osu.Desktop/SDL2BatteryInfo.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b1e1a8f118..3e06dad4c5 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -160,7 +160,7 @@ namespace osu.Desktop host.Window.Title = Name; } - protected override BatteryInfo CreateBatteryInfo() => new SDL3BatteryInfo(); + protected override BatteryInfo CreateBatteryInfo() => FrameworkEnvironment.UseSDL3 ? new SDL3BatteryInfo() : new SDL2BatteryInfo(); protected override void Dispose(bool isDisposing) { diff --git a/osu.Desktop/SDL2BatteryInfo.cs b/osu.Desktop/SDL2BatteryInfo.cs new file mode 100644 index 0000000000..9ca2dc3a5c --- /dev/null +++ b/osu.Desktop/SDL2BatteryInfo.cs @@ -0,0 +1,25 @@ +// 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.Utils; + +namespace osu.Desktop +{ + internal class SDL2BatteryInfo : BatteryInfo + { + public override double? ChargeLevel + { + get + { + SDL2.SDL.SDL_GetPowerInfo(out _, out int percentage); + + if (percentage == -1) + return null; + + return percentage / 100.0; + } + } + + public override bool OnBattery => SDL2.SDL.SDL_GetPowerInfo(out _, out _) == SDL2.SDL.SDL_PowerState.SDL_POWERSTATE_ON_BATTERY; + } +} From ccf8473aae70b4898c7289c2601a07e418e23257 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 23 May 2024 13:00:18 +0200 Subject: [PATCH 548/581] Use appropriate `SDL_ShowSimpleMessageBox` --- osu.Desktop/Program.cs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 23e56cdce9..0d8de8dce7 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -28,6 +28,14 @@ namespace osu.Desktop private static LegacyTcpIpcProvider? legacyIpc; + private static unsafe void showMessageBox(string title, string message) + { + if (FrameworkEnvironment.UseSDL3) + SDL3.SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, title, message, null); + else + SDL2.SDL.SDL_ShowSimpleMessageBox(SDL2.SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, title, message, IntPtr.Zero); + } + [STAThread] public static void Main(string[] args) { @@ -52,19 +60,15 @@ namespace osu.Desktop // See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/ if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2)) { - unsafe - { - // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider - // disabling it ourselves. - // We could also better detect compatibility mode if required: - // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 - SDL3.SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, - "Your operating system is too old to run osu!"u8, - "This version of osu! requires at least Windows 8.1 to run.\n"u8 - + "Please upgrade your operating system or consider using an older version of osu!.\n\n"u8 - + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!"u8, null); - return; - } + // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider + // disabling it ourselves. + // We could also better detect compatibility mode if required: + // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 + showMessageBox("Your operating system is too old to run osu!", + "This version of osu! requires at least Windows 8.1 to run.\n" + + "Please upgrade your operating system or consider using an older version of osu!.\n\n" + + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!"); + return; } setupSquirrel(); From 070668c96f4494958b0e1b4464dd4059e9ba0ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 13:55:11 +0200 Subject: [PATCH 549/581] Use `ShiftPressed` instead of explicitly checking both physical keys Not only is this simpler, but it also is more correct (for explanation why, try holding both shift keys while dragging, and just releasing one of them - the previous code would briefly turn aspect ratio off). --- .../Edit/Compose/Components/SelectionBoxScaleHandle.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index c188d23a58..12787a1c55 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -51,9 +51,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnKeyDown(KeyDownEvent e) { - if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) + if (IsDragged) { - applyScale(shouldLockAspectRatio: true); + applyScale(shouldLockAspectRatio: e.ShiftPressed); return true; } @@ -64,8 +64,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.OnKeyUp(e); - if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight)) - applyScale(shouldLockAspectRatio: false); + if (IsDragged) + applyScale(shouldLockAspectRatio: e.ShiftPressed); } protected override void OnDragEnd(DragEndEvent e) From 9e86a08405db8a88fec2066975843b13ae831eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 14:07:43 +0200 Subject: [PATCH 550/581] Simplify scale origin computation --- .../Components/SelectionBoxScaleHandle.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 12787a1c55..352a4985d6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Utils; @@ -102,21 +103,8 @@ namespace osu.Game.Screens.Edit.Compose.Components ? new Vector2((rawScale.X + rawScale.Y) * 0.5f) : rawScale; - scaleHandler!.Update(newScale, getOriginPosition(), getAdjustAxis()); - } - - private Vector2 getOriginPosition() - { - var quad = scaleHandler!.OriginalSurroundingQuad!.Value; - Vector2 origin = quad.TopLeft; - - if ((originalAnchor & Anchor.x0) > 0) - origin.X += quad.Width; - - if ((originalAnchor & Anchor.y0) > 0) - origin.Y += quad.Height; - - return origin; + var scaleOrigin = originalAnchor.Opposite().PositionOnQuad(scaleHandler!.OriginalSurroundingQuad!.Value); + scaleHandler!.Update(newScale, scaleOrigin, getAdjustAxis()); } private Axes getAdjustAxis() From abca62d5f0e545cd167305d292f09944a6397cd1 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 23 May 2024 14:24:42 +0200 Subject: [PATCH 551/581] Revert "Use appropriate `SDL_ShowSimpleMessageBox`" This reverts commit ccf8473aae70b4898c7289c2601a07e418e23257. --- osu.Desktop/Program.cs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 0d8de8dce7..23e56cdce9 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -28,14 +28,6 @@ namespace osu.Desktop private static LegacyTcpIpcProvider? legacyIpc; - private static unsafe void showMessageBox(string title, string message) - { - if (FrameworkEnvironment.UseSDL3) - SDL3.SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, title, message, null); - else - SDL2.SDL.SDL_ShowSimpleMessageBox(SDL2.SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, title, message, IntPtr.Zero); - } - [STAThread] public static void Main(string[] args) { @@ -60,15 +52,19 @@ namespace osu.Desktop // See https://www.mongodb.com/docs/realm/sdk/dotnet/compatibility/ if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2)) { - // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider - // disabling it ourselves. - // We could also better detect compatibility mode if required: - // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 - showMessageBox("Your operating system is too old to run osu!", - "This version of osu! requires at least Windows 8.1 to run.\n" - + "Please upgrade your operating system or consider using an older version of osu!.\n\n" - + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!"); - return; + unsafe + { + // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider + // disabling it ourselves. + // We could also better detect compatibility mode if required: + // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730 + SDL3.SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR, + "Your operating system is too old to run osu!"u8, + "This version of osu! requires at least Windows 8.1 to run.\n"u8 + + "Please upgrade your operating system or consider using an older version of osu!.\n\n"u8 + + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!"u8, null); + return; + } } setupSquirrel(); From f17f70dca7eeb60690d83435d0bdba399fdd38bd Mon Sep 17 00:00:00 2001 From: Aurelian Date: Thu, 23 May 2024 14:36:49 +0200 Subject: [PATCH 552/581] Changed Size to be handled by AutoSizeAxes --- .../Sliders/Components/SliderBodyPiece.cs | 13 ++----------- .../Skinning/Default/ManualSliderBody.cs | 13 +++++-------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 14d72a2d36..44c754d8f5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Default; @@ -41,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private void load(OsuColour colours) { body.BorderColour = colours.Yellow; + AutoSizeAxes = Axes.Both; } private int? lastVersion; @@ -64,17 +66,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components OriginPosition = body.PathOffset; } - public override Vector2 Size - { - get - { - if (base.Size != body.Size) - Size = body.Size; - return base.Size; - } - set => base.Size = value; - } - public void RecyclePath() => body.RecyclePath(); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos); diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs index 99d954059c..2fc18da254 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Default @@ -13,15 +15,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { public new void SetVertices(IReadOnlyList vertices) => base.SetVertices(vertices); - public override Vector2 Size + [BackgroundDependencyLoader] + private void load() { - get - { - if (base.Size != Path.Size) - Size = Path.Size; - return base.Size; - } - set => base.Size = value; + AutoSizeAxes = Axes.Both; } } } From ac5c031a3a077cc5072b7f6b331623aa91681d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 14:18:29 +0200 Subject: [PATCH 553/581] Simplify original state management in skin selection scale handler --- .../SkinEditor/SkinSelectionScaleHandler.cs | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs index 0c2ee6aae3..08df8df7e2 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionScaleHandler.cs @@ -53,13 +53,8 @@ namespace osu.Game.Overlays.SkinEditor private bool allSelectedSupportManualSizing(Axes axis) => selectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false); - private Drawable[]? objectsInScale; - + private Dictionary? objectsInScale; private Vector2? defaultOrigin; - private Dictionary? originalWidths; - private Dictionary? originalHeights; - private Dictionary? originalScales; - private Dictionary? originalPositions; private bool isFlippedX; private bool isFlippedY; @@ -71,12 +66,8 @@ namespace osu.Game.Overlays.SkinEditor changeHandler?.BeginChange(); - objectsInScale = selectedItems.Cast().ToArray(); - originalWidths = objectsInScale.ToDictionary(d => d, d => d.Width); - originalHeights = objectsInScale.ToDictionary(d => d, d => d.Height); - originalScales = objectsInScale.ToDictionary(d => d, d => d.Scale); - originalPositions = objectsInScale.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition)); - OriginalSurroundingQuad = ToLocalSpace(GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray()))); + objectsInScale = selectedItems.Cast().ToDictionary(d => d, d => new OriginalDrawableState(d)); + OriginalSurroundingQuad = ToLocalSpace(GeometryUtils.GetSurroundingQuad(objectsInScale.SelectMany(d => d.Key.ScreenSpaceDrawQuad.GetVertices().ToArray()))); defaultOrigin = OriginalSurroundingQuad.Value.Centre; isFlippedX = false; @@ -88,7 +79,7 @@ namespace osu.Game.Overlays.SkinEditor if (objectsInScale == null) throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); - Debug.Assert(originalWidths != null && originalHeights != null && originalScales != null && originalPositions != null && defaultOrigin != null && OriginalSurroundingQuad != null); + Debug.Assert(defaultOrigin != null && OriginalSurroundingQuad != null); var actualOrigin = ToScreenSpace(origin ?? defaultOrigin.Value); @@ -132,9 +123,9 @@ namespace osu.Game.Overlays.SkinEditor return; } - foreach (var b in objectsInScale) + foreach (var (b, originalState) in objectsInScale) { - UpdatePosition(b, GeometryUtils.GetScaledPosition(scale, actualOrigin, originalPositions[b])); + UpdatePosition(b, GeometryUtils.GetScaledPosition(scale, actualOrigin, originalState.ScreenSpaceOriginPosition)); var currentScale = scale; if (Precision.AlmostEquals(MathF.Abs(b.Rotation) % 180, 90)) @@ -143,15 +134,15 @@ namespace osu.Game.Overlays.SkinEditor switch (adjustAxis) { case Axes.X: - b.Width = MathF.Abs(originalWidths[b] * currentScale.X); + b.Width = MathF.Abs(originalState.Width * currentScale.X); break; case Axes.Y: - b.Height = MathF.Abs(originalHeights[b] * currentScale.Y); + b.Height = MathF.Abs(originalState.Height * currentScale.Y); break; case Axes.Both: - b.Scale = originalScales[b] * currentScale; + b.Scale = originalState.Scale * currentScale; break; } } @@ -165,11 +156,23 @@ namespace osu.Game.Overlays.SkinEditor changeHandler?.EndChange(); objectsInScale = null; - originalPositions = null; - originalWidths = null; - originalHeights = null; - originalScales = null; defaultOrigin = null; } + + private struct OriginalDrawableState + { + public float Width { get; } + public float Height { get; } + public Vector2 Scale { get; } + public Vector2 ScreenSpaceOriginPosition { get; } + + public OriginalDrawableState(Drawable drawable) + { + Width = drawable.Width; + Height = drawable.Height; + Scale = drawable.Scale; + ScreenSpaceOriginPosition = drawable.ToScreenSpace(drawable.OriginPosition); + } + } } } From f7bcccacb03358667fa40d0603bb01d3233bf591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 14:41:59 +0200 Subject: [PATCH 554/581] Simplify original state management in osu! scale handler --- .../Edit/OsuSelectionScaleHandler.cs | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 7d5240fb69..b0299c5668 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -55,12 +55,8 @@ namespace osu.Game.Rulesets.Osu.Edit CanScaleDiagonally.Value = CanScaleX.Value && CanScaleY.Value; } - private OsuHitObject[]? objectsInScale; - + private Dictionary? objectsInScale; private Vector2? defaultOrigin; - private Dictionary? originalPositions; - private Dictionary? originalPathControlPointPositions; - private Dictionary? originalPathControlPointTypes; public override void Begin() { @@ -69,18 +65,11 @@ namespace osu.Game.Rulesets.Osu.Edit changeHandler?.BeginChange(); - objectsInScale = selectedMovableObjects.ToArray(); - OriginalSurroundingQuad = objectsInScale.Length == 1 && objectsInScale.First() is Slider slider + objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho)); + OriginalSurroundingQuad = objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider ? GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position)) - : GeometryUtils.GetSurroundingQuad(objectsInScale); + : GeometryUtils.GetSurroundingQuad(objectsInScale.Keys); defaultOrigin = OriginalSurroundingQuad.Value.Centre; - originalPositions = objectsInScale.ToDictionary(obj => obj, obj => obj.Position); - originalPathControlPointPositions = objectsInScale.OfType().ToDictionary( - obj => obj, - obj => obj.Path.ControlPoints.Select(point => point.Position).ToArray()); - originalPathControlPointTypes = objectsInScale.OfType().ToDictionary( - obj => obj, - obj => obj.Path.ControlPoints.Select(p => p.Type).ToArray()); } public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both) @@ -88,22 +77,26 @@ namespace osu.Game.Rulesets.Osu.Edit if (objectsInScale == null) throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!"); - Debug.Assert(originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null && originalPathControlPointTypes != null && OriginalSurroundingQuad != null); + Debug.Assert(defaultOrigin != null && OriginalSurroundingQuad != null); Vector2 actualOrigin = origin ?? defaultOrigin.Value; // for the time being, allow resizing of slider paths only if the slider is // the only hit object selected. with a group selection, it's likely the user // is not looking to change the duration of the slider but expand the whole pattern. - if (objectsInScale.Length == 1 && objectsInScale.First() is Slider slider) - scaleSlider(slider, scale, originalPathControlPointPositions[slider], originalPathControlPointTypes[slider]); + if (objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider) + { + var originalInfo = objectsInScale[slider]; + Debug.Assert(originalInfo.PathControlPointPositions != null && originalInfo.PathControlPointTypes != null); + scaleSlider(slider, scale, originalInfo.PathControlPointPositions, originalInfo.PathControlPointTypes); + } else { scale = getClampedScale(OriginalSurroundingQuad.Value, actualOrigin, scale); - foreach (var ho in objectsInScale) + foreach (var (ho, originalState) in objectsInScale) { - ho.Position = GeometryUtils.GetScaledPosition(scale, actualOrigin, originalPositions[ho]); + ho.Position = GeometryUtils.GetScaledPosition(scale, actualOrigin, originalState.Position); } } @@ -119,9 +112,6 @@ namespace osu.Game.Rulesets.Osu.Edit objectsInScale = null; OriginalSurroundingQuad = null; - originalPositions = null; - originalPathControlPointPositions = null; - originalPathControlPointTypes = null; defaultOrigin = null; } @@ -193,7 +183,7 @@ namespace osu.Game.Rulesets.Osu.Edit private void moveSelectionInBounds() { - Quad quad = GeometryUtils.GetSurroundingQuad(objectsInScale!); + Quad quad = GeometryUtils.GetSurroundingQuad(objectsInScale!.Keys); Vector2 delta = Vector2.Zero; @@ -207,8 +197,22 @@ namespace osu.Game.Rulesets.Osu.Edit if (quad.BottomRight.Y > OsuPlayfield.BASE_SIZE.Y) delta.Y -= quad.BottomRight.Y - OsuPlayfield.BASE_SIZE.Y; - foreach (var h in objectsInScale!) + foreach (var (h, _) in objectsInScale!) h.Position += delta; } + + private struct OriginalHitObjectState + { + public Vector2 Position { get; } + public Vector2[]? PathControlPointPositions { get; } + public PathType?[]? PathControlPointTypes { get; } + + public OriginalHitObjectState(OsuHitObject hitObject) + { + Position = hitObject.Position; + PathControlPointPositions = (hitObject as IHasPath)?.Path.ControlPoints.Select(p => p.Position).ToArray(); + PathControlPointTypes = (hitObject as IHasPath)?.Path.ControlPoints.Select(p => p.Type).ToArray(); + } + } } } From 3e34b2d37ed895f9eb707be9921964795c2e750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 14:56:08 +0200 Subject: [PATCH 555/581] Bring back clamping in osu! scale handler Being able to flip doesn't really feel all that good and `master` was already clamping, so let's just bring that back for now. Flipping can be reconsidered in a follow-up if it actually can be made to behave well. --- osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index b0299c5668..75b404684f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Framework.Logging; using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -120,6 +121,8 @@ namespace osu.Game.Rulesets.Osu.Edit private void scaleSlider(Slider slider, Vector2 scale, Vector2[] originalPathPositions, PathType?[] originalPathTypes) { + scale = Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON)); + // Maintain the path types in case they were defaulted to bezier at some point during scaling for (int i = 0; i < slider.Path.ControlPoints.Count; i++) { @@ -178,7 +181,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (!Precision.AlmostEquals(selectionQuad.BottomRight.Y - origin.Y, 0)) scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y); - return scale; + Logger.Log($"scale = {scale}"); + return Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON)); } private void moveSelectionInBounds() From 128029e2af5bd0d23db7935761807487ded1eb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 15:08:43 +0200 Subject: [PATCH 556/581] Fix aspect ratio lock applying when shift pressed on a non-corner anchor It doesn't make sense and it wasn't doing the right thing. --- .../Edit/Compose/Components/SelectionBoxScaleHandle.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs index 352a4985d6..eca0c08ba1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Framework.Utils; @@ -47,14 +48,14 @@ namespace osu.Game.Screens.Edit.Compose.Components rawScale = convertDragEventToScaleMultiplier(e); - applyScale(shouldLockAspectRatio: e.ShiftPressed); + applyScale(shouldLockAspectRatio: isCornerAnchor(originalAnchor) && e.ShiftPressed); } protected override bool OnKeyDown(KeyDownEvent e) { if (IsDragged) { - applyScale(shouldLockAspectRatio: e.ShiftPressed); + applyScale(shouldLockAspectRatio: isCornerAnchor(originalAnchor) && e.ShiftPressed); return true; } @@ -66,7 +67,7 @@ namespace osu.Game.Screens.Edit.Compose.Components base.OnKeyUp(e); if (IsDragged) - applyScale(shouldLockAspectRatio: e.ShiftPressed); + applyScale(shouldLockAspectRatio: isCornerAnchor(originalAnchor) && e.ShiftPressed); } protected override void OnDragEnd(DragEndEvent e) @@ -123,5 +124,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return Axes.Both; } } + + private bool isCornerAnchor(Anchor anchor) => !anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1); } } From d8ba95f87712a7e407a8e28c9c41cc8035b9826a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 May 2024 15:13:42 +0200 Subject: [PATCH 557/581] Remove leftover log whooops. --- osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 75b404684f..af03c4d925 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; -using osu.Framework.Logging; using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -181,7 +180,6 @@ namespace osu.Game.Rulesets.Osu.Edit if (!Precision.AlmostEquals(selectionQuad.BottomRight.Y - origin.Y, 0)) scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y); - Logger.Log($"scale = {scale}"); return Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON)); } From b1c7afd75b273c1f7f12738e1a48db7c2c956002 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 23 May 2024 23:45:04 +0900 Subject: [PATCH 558/581] Move to ctor --- .../Blueprints/Sliders/Components/SliderBodyPiece.cs | 11 ++++++----- .../Skinning/Default/ManualSliderBody.cs | 8 +++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 44c754d8f5..12626a77ed 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -28,21 +28,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public SliderBodyPiece() { - InternalChild = body = new ManualSliderBody - { - AccentColour = Color4.Transparent - }; + AutoSizeAxes = Axes.Both; // SliderSelectionBlueprint relies on calling ReceivePositionalInputAt on this drawable to determine whether selection should occur. // Without AlwaysPresent, a movement in a parent container (ie. the editor composer area resizing) could cause incorrect input handling. AlwaysPresent = true; + + InternalChild = body = new ManualSliderBody + { + AccentColour = Color4.Transparent + }; } [BackgroundDependencyLoader] private void load(OsuColour colours) { body.BorderColour = colours.Yellow; - AutoSizeAxes = Axes.Both; } private int? lastVersion; diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs index 2fc18da254..127d13730a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/ManualSliderBody.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osuTK; @@ -13,12 +12,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default /// public partial class ManualSliderBody : SliderBody { - public new void SetVertices(IReadOnlyList vertices) => base.SetVertices(vertices); - - [BackgroundDependencyLoader] - private void load() + public ManualSliderBody() { AutoSizeAxes = Axes.Both; } + + public new void SetVertices(IReadOnlyList vertices) => base.SetVertices(vertices); } } From a80dbba9d0d4fc836b899b1825692170c1c843d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 10:21:40 +0200 Subject: [PATCH 559/581] Update to not use obsoleted method --- osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs index 5930c077a4..8b6f31d599 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Editing // It's important values are committed immediately on focus loss so the editor exit sequence detects them. AddAssert("value immediately changed on focus loss", () => { - InputManager.TriggerFocusContention(metadataSection); + ((IFocusManager)InputManager).TriggerFocusContention(metadataSection); return editorBeatmap.Metadata.Artist; }, () => Is.EqualTo("Example ArtistExample Artist")); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 47b2a53607..55ab03a590 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -723,7 +723,7 @@ namespace osu.Game.Screens.Edit // // This is important to ensure that if the user is still editing a textbox, it will commit // (and potentially block the exit procedure for save). - GetContainingInputManager().TriggerFocusContention(this); + GetContainingFocusManager().TriggerFocusContention(this); if (!ExitConfirmed) { From 807d982a721ec043990d1871f41316763d3b5b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 10:24:50 +0200 Subject: [PATCH 560/581] Move workaround to subscreen --- osu.Game/Screens/Edit/Editor.cs | 6 +----- osu.Game/Screens/Edit/EditorScreen.cs | 5 +++++ osu.Game/Screens/Edit/Setup/SetupScreen.cs | 12 ++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 55ab03a590..07c32983f5 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -719,11 +719,7 @@ namespace osu.Game.Screens.Edit public override bool OnExiting(ScreenExitEvent e) { - // Before exiting, trigger a focus loss. - // - // This is important to ensure that if the user is still editing a textbox, it will commit - // (and potentially block the exit procedure for save). - GetContainingFocusManager().TriggerFocusContention(this); + currentScreen?.OnExiting(e); if (!ExitConfirmed) { diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 3bc870b898..a795b310a2 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Screens; namespace osu.Game.Screens.Edit { @@ -37,6 +38,10 @@ namespace osu.Game.Screens.Edit protected override void PopOut() => this.FadeOut(); + public virtual void OnExiting(ScreenExitEvent e) + { + } + #region Clipboard operations public BindableBool CanCut { get; } = new BindableBool(); diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 266ea1f929..5345db0a4f 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Overlays; @@ -55,6 +56,17 @@ namespace osu.Game.Screens.Edit.Setup })); } + public override void OnExiting(ScreenExitEvent e) + { + base.OnExiting(e); + + // Before exiting, trigger a focus loss. + // + // This is important to ensure that if the user is still editing a textbox, it will commit + // (and potentially block the exit procedure for save). + GetContainingFocusManager().TriggerFocusContention(this); + } + private partial class SetupScreenSectionsContainer : SectionsContainer { protected override UserTrackingScrollContainer CreateScrollContainer() From 7255cc3344e75ec0e0eb1fd99eab94da1f50f784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 11:25:29 +0200 Subject: [PATCH 561/581] Fix tests dying on a nullref --- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 5345db0a4f..7a7907d08a 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Edit.Setup // // This is important to ensure that if the user is still editing a textbox, it will commit // (and potentially block the exit procedure for save). - GetContainingFocusManager().TriggerFocusContention(this); + GetContainingFocusManager()?.TriggerFocusContention(this); } private partial class SetupScreenSectionsContainer : SectionsContainer From 9045ec24abc5e844da7cc646fcc7fcbb620e75bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 12:04:09 +0200 Subject: [PATCH 562/581] Rewrite test --- .../SongSelect/TestScenePlaySongSelect.cs | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index af8b2a7760..7f0c209215 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -93,49 +93,49 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); changeMods(); - AddStep("decreasing speed without mods", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("half time activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("decreasing speed with halftime", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.9", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.9); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("half time speed changed to 0.9x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.9).Within(0.005)); - AddStep("increasing speed with halftime", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("halftime at 0.95", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("half time speed changed to 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("increasing speed with halftime to nomod", () => songSelect?.ChangeSpeed(+0.05)); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); - AddStep("increasing speed without mods", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("double time activated at 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); - AddStep("increasing speed with doubletime", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("doubletime at 1.1", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.1); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("double time speed changed to 1.1x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.1).Within(0.005)); - AddStep("decreasing speed with doubletime", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModDoubleTime mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("double time speed changed to 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); OsuModNightcore nc = new OsuModNightcore { SpeedChange = { Value = 1.05 } }; changeMods(nc); - AddStep("increasing speed with nightcore", () => songSelect?.ChangeSpeed(+0.05)); - AddAssert("nightcore at 1.1", () => songSelect!.Mods.Value.Single() is ModNightcore mod && Math.Round(mod.SpeedChange.Value, 2) == 1.1); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("nightcore speed changed to 1.1x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.1).Within(0.005)); - AddStep("decreasing speed with nightcore", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("doubletime at 1.05", () => songSelect!.Mods.Value.Single() is ModNightcore mod && Math.Round(mod.SpeedChange.Value, 2) == 1.05); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("nightcore speed changed to 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); - AddStep("decreasing speed with nightcore to nomod", () => songSelect?.ChangeSpeed(-0.05)); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); - AddStep("decreasing speed nomod, nightcore was selected", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("decreasing speed with daycore", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("daycore at 0.9", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.9); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.9).Within(0.005)); - AddStep("increasing speed with daycore", () => songSelect?.ChangeSpeed(0.05)); - AddAssert("daycore at 0.95", () => songSelect!.Mods.Value.Single() is ModDaycore mod && Math.Round(mod.SpeedChange.Value, 2) == 0.95); + AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); OsuModDoubleTime dt = new OsuModDoubleTime { @@ -143,8 +143,9 @@ namespace osu.Game.Tests.Visual.SongSelect AdjustPitch = { Value = true }, }; changeMods(dt); - AddStep("decreasing speed from doubletime 1.02 with adjustpitch enabled", () => songSelect?.ChangeSpeed(-0.05)); - AddAssert("halftime at 0.97 with adjustpitch enabled", () => songSelect!.Mods.Value.Single() is ModHalfTime mod && Math.Round(mod.SpeedChange.Value, 2) == 0.97 && mod.AdjustPitch.Value); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + AddAssert("half time activated at 0.97x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.97).Within(0.005)); + AddAssert("adjust pitch preserved", () => songSelect!.Mods.Value.OfType().Single().AdjustPitch.Value, () => Is.True); OsuModHalfTime ht = new OsuModHalfTime { @@ -153,16 +154,19 @@ namespace osu.Game.Tests.Visual.SongSelect }; Mod[] modlist = { ht, new OsuModHardRock(), new OsuModHidden() }; changeMods(modlist); - AddStep("decreasing speed from halftime 0.97 with adjustpitch enabled, HDHR enabled", () => songSelect?.ChangeSpeed(0.05)); - AddAssert("doubletime at 1.02 with adjustpitch enabled, HDHR still enabled", () => songSelect!.Mods.Value.Count(mod => (mod is ModDoubleTime modDt && modDt.AdjustPitch.Value && Math.Round(modDt.SpeedChange.Value, 2) == 1.02) || mod is ModHardRock || mod is ModHidden) == 3); + AddStep("decrease speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("double time activated at 1.02x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.02).Within(0.005)); + AddAssert("double time activated at 1.02x", () => songSelect!.Mods.Value.OfType().Single().AdjustPitch.Value, () => Is.True); + AddAssert("HD still enabled", () => songSelect!.Mods.Value.OfType().SingleOrDefault(), () => Is.Not.Null); + AddAssert("HR still enabled", () => songSelect!.Mods.Value.OfType().SingleOrDefault(), () => Is.Not.Null); changeMods(new ModWindUp()); AddStep("windup active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); AddAssert("windup still active", () => songSelect!.Mods.Value.First() is ModWindUp); changeMods(new ModAdaptiveSpeed()); - AddStep("adaptivespeed active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); - AddAssert("adaptivespeed still active", () => songSelect!.Mods.Value.First() is ModAdaptiveSpeed); + AddStep("adaptive speed active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); + AddAssert("adaptive speed still active", () => songSelect!.Mods.Value.First() is ModAdaptiveSpeed); } [Test] From 63406b6feb2215111626903bceef923ffb5ca46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 12:59:24 +0200 Subject: [PATCH 563/581] Rewrite implementation --- .../SongSelect/TestScenePlaySongSelect.cs | 51 ++++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 11 +- .../Screens/Select/ModSpeedHotkeyHandler.cs | 105 ++++++++++++ osu.Game/Screens/Select/SongSelect.cs | 152 +----------------- 4 files changed, 149 insertions(+), 170 deletions(-) create mode 100644 osu.Game/Screens/Select/ModSpeedHotkeyHandler.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 7f0c209215..6581ce0323 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -93,25 +93,25 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); changeMods(); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("half time activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("half time speed changed to 0.9x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.9).Within(0.005)); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("half time speed changed to 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("double time activated at 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("double time speed changed to 1.1x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.1).Within(0.005)); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("double time speed changed to 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); OsuModNightcore nc = new OsuModNightcore @@ -119,22 +119,23 @@ namespace osu.Game.Tests.Visual.SongSelect SpeedChange = { Value = 1.05 } }; changeMods(nc); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + + increaseModSpeed(); AddAssert("nightcore speed changed to 1.1x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.1).Within(0.005)); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("nightcore speed changed to 1.05x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.05).Within(0.005)); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("no mods selected", () => songSelect!.Mods.Value.Count == 0); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + decreaseModSpeed(); AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.9).Within(0.005)); - AddStep("increase speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("daycore activated at 0.95x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.95).Within(0.005)); OsuModDoubleTime dt = new OsuModDoubleTime @@ -143,7 +144,8 @@ namespace osu.Game.Tests.Visual.SongSelect AdjustPitch = { Value = true }, }; changeMods(dt); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(-0.05)); + + decreaseModSpeed(); AddAssert("half time activated at 0.97x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(0.97).Within(0.005)); AddAssert("adjust pitch preserved", () => songSelect!.Mods.Value.OfType().Single().AdjustPitch.Value, () => Is.True); @@ -154,19 +156,34 @@ namespace osu.Game.Tests.Visual.SongSelect }; Mod[] modlist = { ht, new OsuModHardRock(), new OsuModHidden() }; changeMods(modlist); - AddStep("decrease speed", () => songSelect?.ChangeSpeed(0.05)); + + increaseModSpeed(); AddAssert("double time activated at 1.02x", () => songSelect!.Mods.Value.OfType().Single().SpeedChange.Value, () => Is.EqualTo(1.02).Within(0.005)); AddAssert("double time activated at 1.02x", () => songSelect!.Mods.Value.OfType().Single().AdjustPitch.Value, () => Is.True); AddAssert("HD still enabled", () => songSelect!.Mods.Value.OfType().SingleOrDefault(), () => Is.Not.Null); AddAssert("HR still enabled", () => songSelect!.Mods.Value.OfType().SingleOrDefault(), () => Is.Not.Null); changeMods(new ModWindUp()); - AddStep("windup active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("windup still active", () => songSelect!.Mods.Value.First() is ModWindUp); changeMods(new ModAdaptiveSpeed()); - AddStep("adaptive speed active, trying to change speed", () => songSelect?.ChangeSpeed(0.05)); + increaseModSpeed(); AddAssert("adaptive speed still active", () => songSelect!.Mods.Value.First() is ModAdaptiveSpeed); + + void increaseModSpeed() => AddStep("increase mod speed", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.Up); + InputManager.ReleaseKey(Key.ControlLeft); + }); + + void decreaseModSpeed() => AddStep("decrease mod speed", () => + { + InputManager.PressKey(Key.ControlLeft); + InputManager.Key(Key.Down); + InputManager.ReleaseKey(Key.ControlLeft); + }); } [Test] diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ad589e8fa9..f8c67f4a10 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -64,9 +64,6 @@ namespace osu.Game.Overlays.Mods private Func isValidMod = _ => true; - [Resolved] - private SongSelect? songSelect { get; set; } - /// /// A function determining whether each mod in the column should be displayed. /// A return value of means that the mod is not filtered and therefore its corresponding panel should be displayed. @@ -138,6 +135,7 @@ namespace osu.Game.Overlays.Mods private FillFlowContainer footerButtonFlow = null!; private FillFlowContainer footerContentFlow = null!; private DeselectAllModsButton deselectAllModsButton = null!; + private ModSpeedHotkeyHandler modSpeedHotkeyHandler = null!; private Container aboveColumnsContent = null!; private RankingInformationDisplay? rankingInformationDisplay; @@ -190,7 +188,8 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Height = 0 - } + }, + modSpeedHotkeyHandler = new ModSpeedHotkeyHandler(), }); MainAreaContent.AddRange(new Drawable[] @@ -758,11 +757,11 @@ namespace osu.Game.Overlays.Mods } case GlobalAction.IncreaseModSpeed: - songSelect?.ChangeSpeed(0.05); + modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); return true; case GlobalAction.DecreaseModSpeed: - songSelect?.ChangeSpeed(-0.05); + modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); return true; } diff --git a/osu.Game/Screens/Select/ModSpeedHotkeyHandler.cs b/osu.Game/Screens/Select/ModSpeedHotkeyHandler.cs new file mode 100644 index 0000000000..af64002bcf --- /dev/null +++ b/osu.Game/Screens/Select/ModSpeedHotkeyHandler.cs @@ -0,0 +1,105 @@ +// 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 System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Configuration; +using osu.Game.Overlays; +using osu.Game.Overlays.OSD; +using osu.Game.Rulesets.Mods; +using osu.Game.Utils; + +namespace osu.Game.Screens.Select +{ + public partial class ModSpeedHotkeyHandler : Component + { + [Resolved] + private Bindable> selectedMods { get; set; } = null!; + + [Resolved] + private OsuConfigManager config { get; set; } = null!; + + [Resolved] + private OnScreenDisplay? onScreenDisplay { get; set; } + + private ModRateAdjust? lastActiveRateAdjustMod; + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedMods.BindValueChanged(val => + { + lastActiveRateAdjustMod = val.NewValue.OfType().SingleOrDefault() ?? lastActiveRateAdjustMod; + }, true); + } + + public bool ChangeSpeed(double delta, IEnumerable availableMods) + { + double targetSpeed = (selectedMods.Value.OfType().SingleOrDefault()?.SpeedChange.Value ?? 1) + delta; + + if (Precision.AlmostEquals(targetSpeed, 1, 0.005)) + { + selectedMods.Value = selectedMods.Value.Where(m => m is not ModRateAdjust).ToList(); + onScreenDisplay?.Display(new SpeedChangeToast(config, targetSpeed)); + return true; + } + + ModRateAdjust? targetMod; + + if (lastActiveRateAdjustMod is ModDaycore || lastActiveRateAdjustMod is ModNightcore) + { + targetMod = targetSpeed < 1 + ? availableMods.OfType().SingleOrDefault() + : availableMods.OfType().SingleOrDefault(); + } + else + { + targetMod = targetSpeed < 1 + ? availableMods.OfType().SingleOrDefault() + : availableMods.OfType().SingleOrDefault(); + } + + if (targetMod == null) + return false; + + // preserve other settings from latest rate adjust mod instance seen + if (lastActiveRateAdjustMod != null) + { + foreach (var (_, sourceProperty) in lastActiveRateAdjustMod.GetSettingsSourceProperties()) + { + if (sourceProperty.Name == nameof(ModRateAdjust.SpeedChange)) + continue; + + var targetProperty = targetMod.GetType().GetProperty(sourceProperty.Name); + + if (targetProperty == null) + continue; + + var targetBindable = (IBindable)targetProperty.GetValue(targetMod)!; + var sourceBindable = (IBindable)sourceProperty.GetValue(lastActiveRateAdjustMod)!; + + if (targetBindable.GetType() != sourceBindable.GetType()) + continue; + + lastActiveRateAdjustMod.CopyAdjustedSetting(targetBindable, sourceBindable); + } + } + + targetMod.SpeedChange.Value = targetSpeed; + + var intendedMods = selectedMods.Value.Where(m => m is not ModRateAdjust).Append(targetMod).ToList(); + + if (!ModUtils.CheckCompatibleSet(intendedMods)) + return false; + + selectedMods.Value = intendedMods; + onScreenDisplay?.Display(new SpeedChangeToast(config, targetMod.SpeedChange.Value)); + return true; + } + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index b3823d7a0f..14e3931fce 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -30,7 +30,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.OSD; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Backgrounds; @@ -40,6 +39,7 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -137,6 +137,7 @@ namespace osu.Game.Screens.Select private double audioFeedbackLastPlaybackTime; private IDisposable? modSelectOverlayRegistration; + private ModSpeedHotkeyHandler modSpeedHotkeyHandler = null!; private AdvancedStats advancedStats = null!; @@ -148,16 +149,6 @@ namespace osu.Game.Screens.Select private Bindable configBackgroundBlur = null!; - private bool lastPitchState; - - private bool usedPitchMods; - - [Resolved] - private OnScreenDisplay? onScreenDisplay { get; set; } - - [Resolved] - private OsuConfigManager config { get; set; } = null!; - [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuColour colours, ManageCollectionsDialog? manageCollectionsDialog, DifficultyRecommender? recommender, OsuConfigManager config) { @@ -333,6 +324,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.Both, }, + modSpeedHotkeyHandler = new ModSpeedHotkeyHandler(), }); if (ShowFooter) @@ -823,140 +815,6 @@ namespace osu.Game.Screens.Select return false; } - private Mod getRateMod(ModType modType, Type type) - { - var modList = game.AvailableMods.Value[modType]; - var multiMod = (MultiMod)modList.First(mod => mod is MultiMod multiMod && multiMod.Mods.Count(mod2 => mod2.GetType().IsSubclassOf(type)) > 0); - var mod = multiMod.Mods.First(mod => mod.GetType().IsSubclassOf(type)); - return mod; - } - - public void ChangeSpeed(double delta) - { - ModNightcore modNc = (ModNightcore)getRateMod(ModType.DifficultyIncrease, typeof(ModNightcore)); - ModDoubleTime modDt = (ModDoubleTime)getRateMod(ModType.DifficultyIncrease, typeof(ModDoubleTime)); - ModDaycore modDc = (ModDaycore)getRateMod(ModType.DifficultyReduction, typeof(ModDaycore)); - ModHalfTime modHt = (ModHalfTime)getRateMod(ModType.DifficultyReduction, typeof(ModHalfTime)); - bool rateModActive = selectedMods.Value.Count(mod => mod is ModRateAdjust) > 0; - bool incompatibleModActive = selectedMods.Value.Count(mod => modDt.IncompatibleMods.Count(incompatibleMod => (mod.GetType().IsSubclassOf(incompatibleMod) || mod.GetType() == incompatibleMod) && incompatibleMod != typeof(ModRateAdjust)) > 0) > 0; - double newRate = Math.Round(1d + delta, 2); - bool isPositive = delta > 0; - - if (incompatibleModActive) - return; - - if (!rateModActive) - { - onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); - - // If no ModRateAdjust is active, activate one - ModRateAdjust? newMod = null; - - if (isPositive && !usedPitchMods) - newMod = modDt; - - if (isPositive && usedPitchMods) - newMod = modNc; - - if (!isPositive && !usedPitchMods) - newMod = modHt; - - if (!isPositive && usedPitchMods) - newMod = modDc; - - if (!usedPitchMods && newMod is ModDoubleTime newModDt) - newModDt.AdjustPitch.Value = lastPitchState; - - if (!usedPitchMods && newMod is ModHalfTime newModHt) - newModHt.AdjustPitch.Value = lastPitchState; - - newMod!.SpeedChange.Value = newRate; - selectedMods.Value = selectedMods.Value.Append(newMod).ToList(); - return; - } - - ModRateAdjust mod = (ModRateAdjust)selectedMods.Value.First(mod => mod is ModRateAdjust); - newRate = Math.Round(mod.SpeedChange.Value + delta, 2); - - // Disable RateAdjustMods if newRate is 1 - if (newRate == 1.0) - { - lastPitchState = false; - usedPitchMods = false; - - if (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) - lastPitchState = true; - - if (mod is ModHalfTime htmod && htmod.AdjustPitch.Value) - lastPitchState = true; - - if (mod is ModNightcore || mod is ModDaycore) - usedPitchMods = true; - - //Disable RateAdjustMods - selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); - - onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); - - return; - } - - bool overMaxRateLimit = (mod is ModHalfTime || mod is ModDaycore) && newRate > mod.SpeedChange.MaxValue; - bool underMinRateLimit = (mod is ModDoubleTime || mod is ModNightcore) && newRate < mod.SpeedChange.MinValue; - - // Swap mod to opposite mod if newRate exceeds max/min speed values - if (overMaxRateLimit || underMinRateLimit) - { - bool adjustPitch = (mod is ModDoubleTime dtmod && dtmod.AdjustPitch.Value) || (mod is ModHalfTime htmod && htmod.AdjustPitch.Value); - - //Disable RateAdjustMods - selectedMods.Value = selectedMods.Value.Where(search => search is not ModRateAdjust).ToList(); - - ModRateAdjust? oppositeMod = null; - - switch (mod) - { - case ModDoubleTime: - modHt.AdjustPitch.Value = adjustPitch; - oppositeMod = modHt; - break; - - case ModHalfTime: - modDt.AdjustPitch.Value = adjustPitch; - oppositeMod = modDt; - break; - - case ModNightcore: - oppositeMod = modDc; - break; - - case ModDaycore: - oppositeMod = modNc; - break; - } - - if (oppositeMod == null) return; - - oppositeMod.SpeedChange.Value = newRate; - selectedMods.Value = selectedMods.Value.Append(oppositeMod).ToList(); - - onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); - - return; - } - - // Cap newRate to max/min values and change rate of current active mod - if (newRate > mod.SpeedChange.MaxValue && (mod is ModDoubleTime || mod is ModNightcore)) - newRate = mod.SpeedChange.MaxValue; - - if (newRate < mod.SpeedChange.MinValue && (mod is ModHalfTime || mod is ModDaycore)) - newRate = mod.SpeedChange.MinValue; - - mod.SpeedChange.Value = newRate; - - onScreenDisplay?.Display(new SpeedChangeToast(config, newRate)); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -1160,11 +1018,11 @@ namespace osu.Game.Screens.Select switch (e.Action) { case GlobalAction.IncreaseModSpeed: - ChangeSpeed(0.05); + modSpeedHotkeyHandler.ChangeSpeed(0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); return true; case GlobalAction.DecreaseModSpeed: - ChangeSpeed(-0.05); + modSpeedHotkeyHandler.ChangeSpeed(-0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); return true; } From 345fb60679c3017f75b1b2d9dd54c210fde50a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 13:08:17 +0200 Subject: [PATCH 564/581] Fix toast strings --- osu.Game/Localisation/ToastStrings.cs | 4 ++-- osu.Game/Overlays/OSD/SpeedChangeToast.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/ToastStrings.cs b/osu.Game/Localisation/ToastStrings.cs index 25899153f8..942540cfc5 100644 --- a/osu.Game/Localisation/ToastStrings.cs +++ b/osu.Game/Localisation/ToastStrings.cs @@ -50,9 +50,9 @@ namespace osu.Game.Localisation public static LocalisableString UrlCopied => new TranslatableString(getKey(@"url_copied"), @"URL copied"); /// - /// "Speed changed to" + /// "Speed changed to {0:N2}x" /// - public static LocalisableString SpeedChangedTo => new TranslatableString(getKey(@"speed_changed"), @"Speed changed to"); + public static LocalisableString SpeedChangedTo(double speed) => new TranslatableString(getKey(@"speed_changed"), @"Speed changed to {0:N2}x", speed); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Overlays/OSD/SpeedChangeToast.cs b/osu.Game/Overlays/OSD/SpeedChangeToast.cs index df4f825541..49d3985b04 100644 --- a/osu.Game/Overlays/OSD/SpeedChangeToast.cs +++ b/osu.Game/Overlays/OSD/SpeedChangeToast.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Localisation; @@ -11,7 +10,7 @@ namespace osu.Game.Overlays.OSD public partial class SpeedChangeToast : Toast { public SpeedChangeToast(OsuConfigManager config, double newSpeed) - : base(CommonStrings.Beatmaps, ToastStrings.SpeedChangedTo + " " + newSpeed.ToString(Thread.CurrentThread.CurrentCulture), config.LookupKeyBindings(GlobalAction.IncreaseModSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseModSpeed)) + : base(ModSelectOverlayStrings.ModCustomisation, ToastStrings.SpeedChangedTo(newSpeed), config.LookupKeyBindings(GlobalAction.IncreaseModSpeed) + " / " + config.LookupKeyBindings(GlobalAction.DecreaseModSpeed)) { } } From 8cac87e4960253eae950f68c88923ad6dcf622dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 13:09:07 +0200 Subject: [PATCH 565/581] Fix speed controls in mod select overlay not handling repeat --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f8c67f4a10..bc87bb4e3d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -704,6 +704,17 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(KeyBindingPressEvent e) { + switch (e.Action) + { + case GlobalAction.IncreaseModSpeed: + modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); + return true; + + case GlobalAction.DecreaseModSpeed: + modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); + return true; + } + if (e.Repeat) return false; @@ -755,14 +766,6 @@ namespace osu.Game.Overlays.Mods return true; } - - case GlobalAction.IncreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - return true; - - case GlobalAction.DecreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - return true; } return base.OnPressed(e); From b1b207960a46337b0a64036d575a23b846f638c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 13:09:44 +0200 Subject: [PATCH 566/581] Actually use return value --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 ++---- osu.Game/Screens/Select/SongSelect.cs | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index bc87bb4e3d..8489b06f47 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -707,12 +707,10 @@ namespace osu.Game.Overlays.Mods switch (e.Action) { case GlobalAction.IncreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - return true; + return modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); case GlobalAction.DecreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - return true; + return modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); } if (e.Repeat) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 14e3931fce..269ca37ff5 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -1018,12 +1018,10 @@ namespace osu.Game.Screens.Select switch (e.Action) { case GlobalAction.IncreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); - return true; + return modSpeedHotkeyHandler.ChangeSpeed(0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); case GlobalAction.DecreaseModSpeed: - modSpeedHotkeyHandler.ChangeSpeed(-0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); - return true; + return modSpeedHotkeyHandler.ChangeSpeed(-0.05, ModUtils.FlattenMods(game.AvailableMods.Value.SelectMany(kv => kv.Value))); } if (e.Repeat) From cab8cf741073959dc314516bac3e3fb63dcc262b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 May 2024 13:14:06 +0200 Subject: [PATCH 567/581] Move mod speed hotkey handler to user mod select overlay The very base class is the wrong place for it because `FreeModSelectOverlay` inherits from it, and that one has different assumptions and rules than "user" selection. In particular, in non-user selection, more than one rate adjust mod may be active, which breaks the mod speed hotkey's basic assumptions. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 --------- .../Overlays/Mods/UserModSelectOverlay.cs | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8489b06f47..d2d7ace936 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,7 +27,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Select; using osu.Game.Utils; using osuTK; using osuTK.Input; @@ -135,7 +134,6 @@ namespace osu.Game.Overlays.Mods private FillFlowContainer footerButtonFlow = null!; private FillFlowContainer footerContentFlow = null!; private DeselectAllModsButton deselectAllModsButton = null!; - private ModSpeedHotkeyHandler modSpeedHotkeyHandler = null!; private Container aboveColumnsContent = null!; private RankingInformationDisplay? rankingInformationDisplay; @@ -189,7 +187,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomCentre, Height = 0 }, - modSpeedHotkeyHandler = new ModSpeedHotkeyHandler(), }); MainAreaContent.AddRange(new Drawable[] @@ -704,15 +701,6 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(KeyBindingPressEvent e) { - switch (e.Action) - { - case GlobalAction.IncreaseModSpeed: - return modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - - case GlobalAction.DecreaseModSpeed: - return modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); - } - if (e.Repeat) return false; diff --git a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs index 49469b99f3..16d71e557b 100644 --- a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs @@ -3,18 +3,30 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Input.Events; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select; using osu.Game.Utils; namespace osu.Game.Overlays.Mods { public partial class UserModSelectOverlay : ModSelectOverlay { + private ModSpeedHotkeyHandler modSpeedHotkeyHandler = null!; + public UserModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { } + [BackgroundDependencyLoader] + private void load() + { + Add(modSpeedHotkeyHandler = new ModSpeedHotkeyHandler()); + } + protected override ModColumn CreateModColumn(ModType modType) => new UserModColumn(modType, false); protected override IReadOnlyList ComputeNewModsFromSelection(IReadOnlyList oldSelection, IReadOnlyList newSelection) @@ -38,6 +50,20 @@ namespace osu.Game.Overlays.Mods return modsAfterRemoval.ToList(); } + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GlobalAction.IncreaseModSpeed: + return modSpeedHotkeyHandler.ChangeSpeed(0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); + + case GlobalAction.DecreaseModSpeed: + return modSpeedHotkeyHandler.ChangeSpeed(-0.05, AllAvailableMods.Where(state => state.ValidForSelection.Value).Select(state => state.Mod)); + } + + return base.OnPressed(e); + } + private partial class UserModColumn : ModColumn { public UserModColumn(ModType modType, bool allowIncompatibleSelection) From 1e90e5e38e22e43c7bbcbb6ee5d11d82aac77216 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 25 May 2024 13:24:25 +0300 Subject: [PATCH 568/581] Use sb element path as a name --- osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs | 1 + osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index fae9ec7f2e..f66f84af7a 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -83,6 +83,7 @@ namespace osu.Game.Storyboards.Drawables Origin = animation.Origin; Position = animation.InitialPosition; Loop = animation.LoopType == AnimationLoopType.LoopForever; + Name = animation.Path; LifetimeStart = animation.StartTime; LifetimeEnd = animation.EndTimeForDisplay; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index ec875219b6..c5d70ddecc 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -85,6 +85,7 @@ namespace osu.Game.Storyboards.Drawables Sprite = sprite; Origin = sprite.Origin; Position = sprite.InitialPosition; + Name = sprite.Path; LifetimeStart = sprite.StartTime; LifetimeEnd = sprite.EndTimeForDisplay; From 76f13b21da3ed79e9daf3d7421342bdb29762dae Mon Sep 17 00:00:00 2001 From: Joppe27 Date: Sat, 25 May 2024 23:28:51 +0200 Subject: [PATCH 569/581] Correct scale of taiko-glow element --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs index 623243e9e1..487106d879 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy private Sprite sprite = null!; + private const float base_scale = 0.8f; + [BackgroundDependencyLoader(true)] private void load(ISkinSource skin, HealthProcessor? healthProcessor) { @@ -30,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 0, - Scale = new Vector2(0.7f), + Scale = new Vector2(base_scale), Colour = new Colour4(255, 228, 0, 255), }; @@ -58,8 +60,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy if (!result.IsHit || !isKiaiActive) return; - sprite.ScaleTo(0.85f).Then() - .ScaleTo(0.7f, 80, Easing.OutQuad); + sprite.ScaleTo(base_scale + 0.15f).Then() + .ScaleTo(base_scale, 80, Easing.OutQuad); } } } From a62b9fa633437bd31dd375bf6c9b12a8591ff225 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 11:42:36 +0900 Subject: [PATCH 570/581] Revert windows 16px icon to original version This also fixes the 48px version looking uncanny due to smaller paddings. --- osu.Desktop/lazer.ico | Bin 76679 -> 76679 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/osu.Desktop/lazer.ico b/osu.Desktop/lazer.ico index 24c1c29ba269aaeb38a9b9c604a1c364c986a753..d5dbf933c1c6795b54b485b349aa76f914707996 100644 GIT binary patch delta 11340 zcmbta32>d&bw)r^mQa#mDQyV}18E9PCbYv$(+)}KOgklj@uGc~S9XF)A%rEr0o#%# zOR^-(vi8lkuq|6&WV`^jF<@g``@ZW*deVDZpY~lk{l0tO|MVmbP1`$j-hc18=bn4+ zx%+otc={UZbpg(~;K|cZAknoR# zo4$|8&p|A71vHlM-q1hXHj3C~kOBG`#9sA7pmC5Zj1A35XZ@(uelZ|Lh23)T zpiM63+f0#V2}X&^~3V;ddI83A#VwsKd)ZBpB2QQo&j~B1gA3OSE5! zeDK5-+5FopvUS;2R8}P6%fEq;w{=;OY<}{pDih;hDktLG#NNEfIjqC~y2T;kyyIUC z@V^7v4pL!sU|LQlT4dYGV$@$G5zC8Z+lrDugBHuN+y}qCED_5}CDyM(K1sF8*r3b% z^QbHW+949d`w$W>*k z%y!QTSX3tIFV%^qY}l;sW=1$rZ-OC+n z@aI#NZ1*geh-XTa9`|xlD=JhSWvBXnfVy2F8!`Im5d)EEll?`WO}0K&0!^i|ZMjKh zY7|5)E0JigN{RKZa#A(ECeh*rQZhc(@KNcKGG^H-iS~rmt15pWmk;-t_46A6tA1u6 z!ZFPLl+us-W&289!o8Ll)D8`@<;fy#32Kb>sZq4U`PJ#g{Oz9fdATgeEaW@o{~FDy zmgrSgQg*tZHZaf_KUnwRKO@5Ftn6+r9g#TST8Z_lfyUZJL_XW8Myq#C!mlUqPFOK{ zqdVP#B9Z7M7{lxbXt%ef7|&|i;aek?@)1;~0_WS0V8V|LMD~$2>X?y?7n>x;vleF5 zeT`_+_LY^&tSEf1C9{aAM0{YohvM2dW-Qqo?icb|#bu<~A7 z`5Ta$pP#1KBzi@yP{Y^AuK|kCTyNwf&ZftDF}_$f&ZkcCnMaCTj^T|mW?FrnY3O=L zDT-Qw=6lyE<*XoTS+#tc+6B8*;NtnOu=Y*^5f9KYkl33hBq5*ymNrO&Un7WLO|&r1 zryj~{TzQmZ3z(U&EATJh55`3A~`8)laB+k1YmemX6CSS|eNxV;kGJtts zBdx>G87k3;rq#Lx<4`#2nOZrMXrpZm+&CUuyj5Ix9e7)q$<1(gSThV{WzCBy5$Sjz zxYDyu<;SBDnhYI@S%r}T0nKbu-yL2Ur^~Ar_^$j$+3B~q0<*$6Z}d0hnAVjwOHyc) zI2LwW!@U7KJ#a-YWeZ9Q9kOE;wsQZLuTh+Lql7aXvsH?3EUk=+s9=W|#sOT(d>U1D(m@*;cuD8O{a8s!Srv#e22Lv2fRX<;m(Nx&i17BzSa_F~_VVuWpt^KMM+K+Ubkd2bM|E zDXUCQaGvWhHasa84p^{tm&5p0i3_NbFAlZI=%AWNrkL0*!;#+QdWWa|?Za%Q(x3C+)mG+!B$;v)`eui_yW zikn8%%2W{5q|*H9Zl093vT>OjM*)P^3Wr)p^Iq=|>R`B9&@VB|8zm{Q9o^inylQWB zRnD29X9Hx_nWpaZVcF@8@!;Dky8}BU(Wg!FKWI_P*zzME*d);xiu~H-bbPnWOwWVn zB;%C^+57trX}K~k$0O_#{Y<_5={Hv;{k3wLo1K@*(OF4aQ!CBIgGyJ;XM-4g7@S^4 zn-pyBl$ohHnV+ARy>GV2PE>mM{dV~zrAyM+wIhwu?BAvgN`P?<7hRiardt=BqUMt0 z4%z7i*?zAim(R0e(qtTS@#gI={i^jV#TZi9z>t%j~B$Ra(hx@9MK zU=vzdFZLGQ^8EKOc>Ju&rMm;H5(gJvJv?CQKI_&6ZVl*=)Zi|qn_j(gs87k4ecU6_ z%j?0nNlIWB_8X&CTFZ6oKHk+WyF)QQ9eSSkTBf9=U|5cBwo8IPRzuG=NeXTevjs=O zF%W&Mk_u(J*#L`fKw-2M1GriGoh>~c*(piBoiNCTE@;OXXp^D>=W1%;{^{0&(=@aA zC`P9@6bIWS4YUh|3pUx*0G%A4kwe?CF?r$M;8QB6lAAHY=VW?fPI6wdO3uq2!g)S1 zIwMINaI*bREVJt>Z(n3t~RA?a=(F-#?L zdB1M;ZY&mDTo~G_2c-I z26RcXpIsTjzEAY-kcKZgjSW^KS#B-ZN(Prmj{Y=Qo*vSpC@r{0GFEp>alsJw3g;3B z*`e+kxtKd7X+c^x+21Y)-|P`v?UZXJoSB@L)}nFw@Ug;5#qnU(#<>)o)v zM_I;J)Sexck-k|u^v6C)4(@QZpfJ+rY5@&O3F;Jkqi!v1aG%BjYyda2?B_580_>6z z(yJ-7SJH#A*ZA6H&x^fs>^+AVN8TNf?Dgm$f3(g`knYL|?UDR9`sJuGAO~LSSITHC z4b589D|?>r1COolg{Q!$2SU3QKRu{V(pGo6x}-4DF1vkfFaX{R>Xn=qx&V628Z?%{ z%dHC@MtYJ5iep>gE*L_qGei49eTp*hO%3Rgv_Kf(2Zh0!uk$I_uNspU#L8d@#0jOv`;dE4g7>46M`(HS^(=ZpfDo@O4iuL(WQ}+kD_3VZ?}@8w+bU%w@yxc=Yc}4 zq^=`f>G2`u?u7;np!|Sjfmm<4p{(hXtT1Nus|*S#0&k&UzbmeJkO_l4WJpv-(q-uZ zt%JO}^W!pT!(jV8JP@SIa13w?8?cXJ?NcNsNe#|dy zcU^4G#ACepa}3d2(6@R}QC6to5T%Tjgn>z7rWtpBl{-;s7SfDgONM2#pog@yJkuG$ zC`WzJpW@dmpYFn@w2VIH@fjeZX}S3YkC(|pkk}d~BrVhi<+_rb)k8vJW#%_KEa~V? z9`_}U+~jd@(z%;FzW1my#VP43K@&i_)S#q?*`%k%wUQLzR(Owr8vhNX&YI^^F!Nz= zb{MN1c71aZH`BSRM}%qDcMan@ox^e;hkxaq9Ys zE@(C%3NwNnvTvguXol;wz6X{n%;iBCsTPjbk}-8A$POKbHDg~RZKXSZRQ5tHclD^U z{#s?O&4abN#rNyNYsZ9@riIv~ty~{kU=%M2gnu7QC>{HrQF;QS6q^T2?p-^+h|F59 z#b3(%pVHLD7Ez9MvCfK9L$|>g6$qIH!YT#m#W|Tcr8=-zrZugnBP6{bt-j2x204c*irAzAfYw4z?#PlM7SfmdXIDT zY%+G;P@FLW9k6IZK3uOY`f&XW;tRC@8{(A7Lkrlu=>ZO$J8W`3wNK4|)MbWfL_EF& zE1obATMcq;+FVl4#A8#y?w0D0jVE|mS)zR7tabnTSvPC6A6O%0StfIxP0l3rs^uBF z%|N6f#9YRmUp#932VZf13d#ey4v+(#xMv(2#9^mfa-K&Y;mnyDfD;w`njSEKF9~N8 z@{mUv%9b1-lmX}21p07ZQul7U{Xsq%c_H%eAcB$l1cL~}=m6fMfECnT7*-cnP9_o+ z@&?X4g2GDz_oyRDw?E+VMsoKv6W&b81YyoAicQ3wunZ&m+ zLP?aVzo;)y^D+yCvKGo7VZ(2MMQQj-d&QWfzgi<(o+<|Mv1`dg*UE)7^WMj!T=>%^ zlKVyjARgC}o(xqtL-~VYRE{B1K;;+rcdIuQzG3mz$NjbETJdPSTSczGI}BcwA_3d+ zd+DHS9_2eLx%W9lxDTg;6rQy8$x>B-PbJ~Y@CFG4EEZ2Je1b6_fz0O^@+lYN_COQ^ zz&dhyrJPN-Q6<70JJACzx`&mmC+pIIUWIz{&4ceTJMdycoRDxE4d5;LF1Ehu(wKT5*lFVb_!2QEz)Z}ae7*&c zXUgor0|a^I+kkopFw67(05HEs-5UYT0HXV93`^ro&+zc@9i|byrEu+#BY^6NlA54( z9MH~)CowAV9>X{hZiR$DQ=@oM_>xS-JbFc)YdFgGLmfqG$86TOJv={5; zOiCL!2^pb00ExcNG1(PXFPHQCA&&>4O#nL;`f3qd^(?w!noIfh?El;6WU{B~^eE6P zcmN1#?rRpA19*LLUq6tNcKPu2F2JmF^5uS@A8&UG!6hyx`Qi0S`H3Cv3e0G_0&K{~ z0vW5~f0AsIq>yGQ2yc-jplCaNEfP=6@#9fkKPI_6-p2mF2iA;U%^y5zp&CkPfz_!cq z9u!zF{axcyae7FOMd(391bqM7?UMFv6R^crbj6H(b+Au%dRirO1Ktz>Okc?9k(04( zDDdZvost~T1q0fo`0y~hfcUV%4exfiqqp)D`pmma9})m4wwF)H#MqP+pY9TC!;r!O z_rGqJ0>iGx#-#$QRGjO^1Js1%{;myh{j3xL+TXdVQ?fR6DfIkO-hdEEV|VV`2&4=E zRic*_T`=zK0z>V(H^7?m&PqV(zCgx_`2aP^dA3K&j*Uuh^Q^R9nUrJi4=89RHOMX( zQitIBY3Zz-mUBrw7u$iTj>z%N{iuZ4Aks%hq~P6tbrRDC0GdS}jo=`hk*N~)|Mi>- zn1)%?qoR&o1d)jM5c^5?1r~&K3a}tzJ>(NhNOq=)6)}&<32^|01vwi)ezH%moZC%I zd0z51!Z7H_-e6bL5Ot<~4PGnU>JBInYQb4e8KmJD;yWg!lj%f1H1e?+vQdBpWQh$N z`h!DCkKk&yu3w$^PDB77P+`{aW$PJ`z6;A^fAhr&vdL^TLxWE0(R-tf&Y z`IJ+#i}J`@041_Q&_y_G5@Vp3S}spw0M1}5G~`ZQz5c~!6a!Dmp|^mP;rvc0!Gsj_ zSKqsNKQ_tC*D!=TUjrGGBf>z+3>uWf@8~TUbwxQ}i55(N7k-Ehsoz>Tsrr<+SzaF1 zC7)jv=Ouk9&%D(0yO6Ig^1S@3J3Q|17#mzOPzSB}Tb==M;Fx=s-Es9GuwJ-{_W<6< z_u@KDWPo2q>Mp-Hy*N+b>GeH+OzA2*tPg}xzs@|}V9lS)VucsdalOG6hu0)t9eF7w zsmtx3d7(A0NuOuxJ1lhbn8u^jz4swvFLFyMZ`}+knozQb-|iD@ z~fS6*E$GdQ6J)M-(yMSvey!T>BBQ?BP&07lr73Q1S#X z9Dg+{vw=Jn;DS+xg&8$WVJ#<3_F&B_LtX}Ua2{Lk!Yw7}9z=LJz8%CkiE$6!a~}0b z`q5849=m6148`%xJT<9lViaHSQ0tHXo)Lz|vmz(o#diI?$l;f9d)lOV%ySCgX;6#3 z27e&LusyKh+Qd9tHK2CLF3hd#uA>r`{ibqD`IKcDQwQuYzO~||3TOxe6;Kgdc;YRQ z&th@pdsP$TA8*k{nR(<<7x^s1vTV#}v4+y;`nj0wib}A7hVM-tl{2s+ z5<^k0{+fAAQ=VO6_FIMFti%*D4WO{8$jmd->`yA75ua?;m6*S#4AX2agIV4*mSe&k zFb3U00n=rS)0OUN@<^s~(}nB?R`6|O$1KY)yTdesWf6@3_aLH{|N9_L_}FpFq6u@# b&=ZDEW}Izh;E>_$Qd7_DezO;xnO^@5U$|tB delta 11404 zcmZu%d2E%}m5-|?N+z8V9i>VlWi&*SQKwb^V5Dd?f3zwi&1fN3D zNr>8yCQvZ8v5jq>7x1$IW45pbLmC2(X%fH~jCbRO=Y7Y!!9dzGzu&pv{ob?FPde{z z=bm%!x$8OSZL9m?wz?mld~*015t#{?9Rjmhmv(9>eFidwa6lfQJS}zPAJXzh9{byJ zlv3Q589@EuaTG`+&qet=pb;K>bO^O4e82v2*!urQj=YKiQ@-BYcctu1ubkN4EQP;o zkgRnzlDW28A!}W=*4NMqW^@y>W;>X-J_(61cSQ-%OQKlVMD*eF})T$L?zNq zXIiT1)|Xjt>R2Can~L|z|6YaMH|1P<>vVYnU^L4WCGGQ}U-=TA4EvEo?i7C8tJfty z{JJD9r~p(-BGOPuTKN6C&~}^OI_H|#FD)5vYiL z?=`3lI~P{TA6|h>NRt*2Dov_oH?MWfR|@eJ*KN1`6X%;TX}gPm*lCuLUY)QfEbln1 z$`_4VC`Qg3^-6N*!fK?|k}wbJV(v}NlT;Ac&a%M^)?1uvNt|B^^|EiUj``$8rth9N zZiUX*qZBC`;{3uvW)^^oQ;glQRTBSdxq;V^5>Vdu$_?52a+ykzyr>40U07IS>X3%k zC9@1*f6ZI`sp+%YY44j2VE%wI*pIvY1qY=WfB-G`NhU0m<=Fc_6WY*NrR=3R=^WYT;>wU_3<(`cD9^+TlRevQm* zmiS#h(x(Jd7u7345;b5L>~smTF8Y9)-w+B#Vm8eGgmdrE)elxNrTC|Y0}&plv&pRMzz=A>b9Z&XWF>!<#Mls?VK>TN`}pzrF_MZ_9+J$Pm$+$ zcPsZaN(V?=0_g!vS(iNDsLYm`QgxLfb5kNzn)W*xxd+xTf3?s&;?q0K_o|fF4g*jt-~*DjB3pTiU1x zo?fK5G`HiZ*|`7*pVE~+0+hd*?!)wjEZnunH|$C8B1>~@L<^z z{*B~*P$orLb%0vQe($Pmez_PoL4^(=ZE=$W=Gc|EIXBO_D4FkEmeal(g`j)~p!k-z zDikFK-(nmAXw9@#4}Z`hnZS#G?S|YNf2h^hi?E*|xNlh-+S&na!XwsxR6m7qvRB^L ztA>xnZF8?k!iq~+C7l*ePA9a<$@oA>8N?zCQ3_Y=4<3cG>ergO>SodPh4(5Rk0p9g-DcfLFNvK2OJ-<8(>J(=BcF zHMw!V>wAlD1XGh9Q7=WQEw-ENS=XR+GnTaCh;;&Tc2}F%n6;)BM^>|(&uP`2aL(5x zDGM4TV{xna*9D27!)zC{@BWQOK>r3^)mm5ZQN0c-JEBu8@TqIRqSRK|^%$nTE02S1)QY*Q^`z#xT=3gkiy%jSi}x39tKnXA9&oEt=u=yIB`1dS4(*}O?xihK zw5!$Y=U?9}IV-vV-8zuag_!#$&yL&DUhUWsOz0xoy|_)a;Zd13f_)p>B?lwOUePJF zB_oQ))xYl}JlE%!DR5nIXYu1!^}pVUIlpbK2PwgT6Java0?KgKlw<$5OHmqj1orYD zL)4u6Mw2^&9x&{hvXwXmJY?E&c=2PJu_PeACGFD(z!g7Jk??bTGlY-mmOU$ar68_L zoY14PyLnUsm3J(zreEg|O6JlI@hxqa#w)=Qe{-TmYQOEBviiA*&+Q5xW_jxalD)iJ z_C{i=Fb!Y&dOh0tZ?)i@Xnkqg2wJf9I_1#&!NrCiGu^{SJD*MJm8@l*N;EgBUyg0+ zrJA{zpT2rzNcKebpsi2gz*}8V`vYC*WyeP_@-8`>7!<<)I@SlYVeot9Y(lW@U|ctj zfObzlUKKtZY8=P~U%xjH%Ek{BdIVRT1E1wxvUf$F049jO*26P(9 zC}(*OXqiEuPZ>e`knD}>(^HuDo1S^h@J!$cD$bgzq|`=!9f2QZ>2JBOcqFI-McolU zAUASAM@2#X(F0)Gi+YR@FeN3P4#ur#*;#?nag<1jOoD_xkX}#B_Dm-g8u{&YF8rKjw0EaF?LLeVgeN%uq!9T*%~_zG&fDf#-Yv@vx^Bhw5TbR}AlE_d8)%j7?F`n@YcnnOxHS$|(;V_N^QN z)e$49@8;uSKYdm;2!?D5!l>l?zDDzN{z%i5A3dUL{$ghEOr=XPXfjhpT+v4LB|SyaWL9at z3#9ZIP8RHAYGnUPaE~^WnI{KR9@jj}`&W(20f5ctN1>lBvya(LCJ4xj&AD4Zoi`r6@6DkT*%g)r--n5VQKa|3_9^Hlql$(+AV9Y&8 zcMqKIYI-mhc`X~R+UDYTxV8`RLfULzrHq+)R(C}KR1>lwIp4D&o%%VWP|%Ot=pdxy zNyFX;VO5Nrsr7(6&Kx|QHUr=_2SQw*{CAn zhXx~Gsl4{M-U^|c!r1!;VtlXsQ`pa6Jmi)S#Xj)b$dRjjFAkxjAL0cQ+>azpuk$Y* zWTF7x`Y(mumcIrQgcFO?ZiYc-B>X_g;Lz%aQ&ZF();iR$Htj}0s0lQ5cUo}c!G zak&5Kpq9_e06@Qy{s*|X@HD^^gOkBwbMcX?a)^DX9Le?Y>*Q)Vo8jA`vu{o$9$KI^ zZTJB&n(3a~2uu8zgEH^TWPtZk^*kJMV>|SX#0#F9(QWS?1$R+4CllMaTXXPuFf!*1McjS+KE77i!_#;Ddd~<#9HtM{(ee zVbtl-Vo;!Cb?uLkA=MLrAv7^8=Ec0i{5nrQx?sgxq#T!Yc;ILKu!=IkvS`k6CoCIg%%_7v~@|8xR0U<%I$9t*(TBunaD{ zGT9Dq8eyAFwH{@-^H^7~sa~p!y*oHS&COZ@LL`rQzAudK>m!T|frk6VLEIOjQJ}u04`XJm#>RUR=F&|WjOj4^WZQyy#xRUK)3#+q z)qs{UDnWKLm^>GY40WOnsmrUq&<;a1AHRga9*Ed@ho)`_$L53Whz79D4dv{H3(hk0 z-YYY}D8PN?TX{>JEjH{z#|u5}bfD5#5?XKa2atmT~em54Yut-s5xcT70LVXrew$6kR9;cQy2{it-2UwYMQdiRy zydA1vE*~C%&!a`sBCDkC@{~Z7lz-WUjan%_c$1kA{f6K>&4}ezs8wG}Xxr^dTr_Xg zsT0?_F?}l=6i}k#K+zsW^?}I*prSr zU$k*oHkFRRW!0)a3TlPhhbJuK8C>ZM59CA5H(bB!#oR9aa|7wn<;lK`0&&zWRU;a5 zU<8| zmjepi0W(R7Bd2GD?jU+-Xbl#|(N_Tvj(T%UJ=^J*Qh{7gjOP@Kg6|7;>d$gLLZn53 zC;;zMzC2wmJLIcA2BhWvt@l-R#+76|HheHzR}UY1EHkAL))Arts}IC@Z%lvYt=9&A z3J5{}*Fft7Omi1pPn=dq0_Gr84~;HF+9f2YMl*a9hKTiFAa(s0)>T0NgzPDa(to{%IbQlU_?vB}l?g&9N3_^;dY)+Z2br&UeC_O0~{fPlyk^bz`$ z_u%5{IIwO&sTzCT*oJEPTKip7*E;s^tyHyKY4Xv6B_2}1ftYbQ3>}5IO!bk<7w|?h;ni_^3;eLy zG!)AkqsBV3Hlu4t!eX=bBu!#27p{U3E6u^<^cPbe?6oDMa%QX9l^m~{gVrcTs?tJg zYjhUA-HWw1O4<17gZvm^J@Wj8Buuf#R0hO3Czz1sLeDh%=eE!4g2MDC=)LQ zQ0#&qDeGvzJohiiL=SaM1}^#$BYU@AUr@X7M?mKJf4Y0)-v!tK$WJ-QY;L@YIKtXG zylO)J$fkcrGk;o3hehCCCR26?iPU^Gtgjuuc9U-)7Jst90Qe@{6;UZAe!U^7uDnAZI2}@Yx?66SPDmF*QU~7;=nJLp zt1&743}GZZE&^qD@!G-v5)kDZzqhaO_=k z-s=&IOt4A*DaV+z~nH>Lf0L*#Is<{>2a(xn=(1;VkI2(;aQV%Zdd~<-& zK{<+;mcD+JIoAO{5OY|6_{}lz=rZ0Y_=4O`fBZM&(o!}FRB=o%{-&KD`WtSTaLRP^ s#9jI9cVi%f_Y1BX=D$5KEF(R-VWPRaxcz3Z1Hca`b|D!lchrLa2RxK$m;e9( From 0d6adf160bcff5da41e5e2262b7e223163670179 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 14:20:28 +0900 Subject: [PATCH 571/581] Share scale factor with hit target --- .../Skinning/Legacy/LegacyKiaiGlow.cs | 8 +++----- .../Skinning/Legacy/TaikoLegacyHitTarget.cs | 10 ++++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs index 487106d879..9877efa127 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs @@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy private Sprite sprite = null!; - private const float base_scale = 0.8f; - [BackgroundDependencyLoader(true)] private void load(ISkinSource skin, HealthProcessor? healthProcessor) { @@ -32,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy Origin = Anchor.Centre, Anchor = Anchor.Centre, Alpha = 0, - Scale = new Vector2(base_scale), + Scale = new Vector2(TaikoLegacyHitTarget.SCALE), Colour = new Colour4(255, 228, 0, 255), }; @@ -60,8 +58,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy if (!result.IsHit || !isKiaiActive) return; - sprite.ScaleTo(base_scale + 0.15f).Then() - .ScaleTo(base_scale, 80, Easing.OutQuad); + sprite.ScaleTo(TaikoLegacyHitTarget.SCALE + 0.15f).Then() + .ScaleTo(TaikoLegacyHitTarget.SCALE, 80, Easing.OutQuad); } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs index 0b43f1c845..2a008d81d9 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacyHitTarget.cs @@ -12,6 +12,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { public partial class TaikoLegacyHitTarget : CompositeDrawable { + /// + /// In stable this is 0.7f (see https://github.com/peppy/osu-stable-reference/blob/7519cafd1823f1879c0d9c991ba0e5c7fd3bfa02/osu!/GameModes/Play/Rulesets/Taiko/RulesetTaiko.cs#L592) + /// but for whatever reason this doesn't match visually. + /// + public const float SCALE = 0.8f; + [BackgroundDependencyLoader] private void load(ISkinSource skin) { @@ -22,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("approachcircle"), - Scale = new Vector2(0.83f), + Scale = new Vector2(SCALE + 0.03f), Alpha = 0.47f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -30,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy new Sprite { Texture = skin.GetTexture("taikobigcircle"), - Scale = new Vector2(0.8f), + Scale = new Vector2(SCALE), Alpha = 0.22f, // eyeballed to match stable Anchor = Anchor.Centre, Origin = Anchor.Centre, From 11c3d11db9b66097d61f877cb2e9b602bf90b5e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 14:38:43 +0900 Subject: [PATCH 572/581] Fix cinema mod not hiding playfield skin layer --- osu.Game/Rulesets/Mods/ModCinema.cs | 2 ++ osu.Game/Screens/Play/HUDOverlay.cs | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index 7c88a8a588..0c00eb6ae0 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Mods { overlay.ShowHud.Value = false; overlay.ShowHud.Disabled = true; + + overlay.PlayfieldSkinLayer.Hide(); } public void ApplyToPlayer(Player player) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 9d7a05bc90..16dfff8c19 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -109,7 +109,10 @@ namespace osu.Game.Screens.Play private readonly List hideTargets; - private readonly Drawable playfieldComponents; + /// + /// The container for skin components attached to + /// + internal readonly Drawable PlayfieldSkinLayer; public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList mods, bool alwaysShowLeaderboard = true) { @@ -129,7 +132,7 @@ namespace osu.Game.Screens.Play drawableRuleset != null ? (rulesetComponents = new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, }) : Empty(), - playfieldComponents = drawableRuleset != null + PlayfieldSkinLayer = drawableRuleset != null ? new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.Playfield, drawableRuleset.Ruleset.RulesetInfo)) { AlwaysPresent = true, } : Empty(), topRightElements = new FillFlowContainer @@ -247,10 +250,10 @@ namespace osu.Game.Screens.Play { Quad playfieldScreenSpaceDrawQuad = drawableRuleset.Playfield.SkinnableComponentScreenSpaceDrawQuad; - playfieldComponents.Position = ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft); - playfieldComponents.Width = (ToLocalSpace(playfieldScreenSpaceDrawQuad.TopRight) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length; - playfieldComponents.Height = (ToLocalSpace(playfieldScreenSpaceDrawQuad.BottomLeft) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length; - playfieldComponents.Rotation = drawableRuleset.Playfield.Rotation; + PlayfieldSkinLayer.Position = ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft); + PlayfieldSkinLayer.Width = (ToLocalSpace(playfieldScreenSpaceDrawQuad.TopRight) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length; + PlayfieldSkinLayer.Height = (ToLocalSpace(playfieldScreenSpaceDrawQuad.BottomLeft) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length; + PlayfieldSkinLayer.Rotation = drawableRuleset.Playfield.Rotation; } float? lowestTopScreenSpaceLeft = null; From bdfce4b9dafc90314e6b55f448e899d164ca1d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 May 2024 08:26:00 +0200 Subject: [PATCH 573/581] Fix xmldoc reference --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 16dfff8c19..0c0941573c 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -110,7 +110,7 @@ namespace osu.Game.Screens.Play private readonly List hideTargets; /// - /// The container for skin components attached to + /// The container for skin components attached to /// internal readonly Drawable PlayfieldSkinLayer; From b6471f0b9cd4ebb3dee155990e89b44e1e2a14f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 17:09:35 +0900 Subject: [PATCH 574/581] Allow previewing audio of playlist items --- .../Drawables/Cards/BeatmapCardThumbnail.cs | 5 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 5 +- .../Overlays/BeatmapSet/Buttons/PlayButton.cs | 5 +- .../BeatmapSet/Buttons/PreviewButton.cs | 6 +-- osu.Game/Overlays/BeatmapSet/Details.cs | 1 + .../OnlinePlay/DrawableRoomPlaylistItem.cs | 49 ++++++++++++++++--- 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index cd498c474a..e70d115715 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.Drawables.Cards.Buttons; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Framework.Graphics.UserInterface; using osuTK; @@ -36,14 +35,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) + public BeatmapCardThumbnail(IBeatmapSetInfo beatmapSetInfo) { InternalChildren = new Drawable[] { new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both, - OnlineInfo = beatmapSetInfo + OnlineInfo = beatmapSetInfo as IBeatmapSetOnlineInfo }, background = new Box { diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 0b1befe7b9..364874cdf7 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -16,7 +16,6 @@ using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osuTK; @@ -26,9 +25,9 @@ namespace osu.Game.Overlays.BeatmapSet { private readonly Statistic length, bpm, circleCount, sliderCount; - private APIBeatmapSet beatmapSet; + private IBeatmapSetInfo beatmapSet; - public APIBeatmapSet BeatmapSet + public IBeatmapSetInfo BeatmapSet { get => beatmapSet; set diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PlayButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PlayButton.cs index 5f9cdf5065..921f136de9 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PlayButton.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; @@ -28,9 +29,9 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons [CanBeNull] public PreviewTrack Preview { get; private set; } - private APIBeatmapSet beatmapSet; + private IBeatmapSetInfo beatmapSet; - public APIBeatmapSet BeatmapSet + public IBeatmapSetInfo BeatmapSet { get => beatmapSet; set diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 2254514a44..1eff4a7c11 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.BeatmapSet.Buttons @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons public IBindable Playing => playButton.Playing; - public APIBeatmapSet BeatmapSet + public IBeatmapSetInfo BeatmapSet { get => playButton.BeatmapSet; set => playButton.BeatmapSet = value; @@ -32,8 +32,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons public PreviewButton() { - Height = 42; - Children = new Drawable[] { background = new Box diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index d656a6b14b..7d69cb7329 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -68,6 +68,7 @@ namespace osu.Game.Overlays.BeatmapSet preview = new PreviewButton { RelativeSizeAxes = Axes.X, + Height = 42, }, new DetailBox { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 1b8e2d8be6..b28269c6e6 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -22,6 +22,7 @@ using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Collections; using osu.Game.Database; using osu.Game.Graphics; @@ -32,6 +33,7 @@ using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; +using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -81,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay private Mod[] requiredMods = Array.Empty(); private Container maskingContainer; - private Container difficultyIconContainer; + private FillFlowContainer difficultyIconContainer; private LinkFlowContainer beatmapText; private LinkFlowContainer authorText; private ExplicitContentBeatmapBadge explicitContent; @@ -93,6 +95,7 @@ namespace osu.Game.Screens.OnlinePlay private Drawable removeButton; private PanelBackground panelBackground; private FillFlowContainer mainFillFlow; + private BeatmapCardThumbnail thumbnail; [Resolved] private RealmAccess realm { get; set; } @@ -282,10 +285,23 @@ namespace osu.Game.Screens.OnlinePlay if (beatmap != null) { - difficultyIconContainer.Child = new DifficultyIcon(beatmap, ruleset, requiredMods) + difficultyIconContainer.Children = new Drawable[] { - Size = new Vector2(icon_height), - TooltipType = DifficultyIconTooltipType.Extended, + thumbnail = new BeatmapCardThumbnail(beatmap.BeatmapSet!) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 60, + RelativeSizeAxes = Axes.Y, + Dimmed = { Value = IsHovered } + }, + new DifficultyIcon(beatmap, ruleset, requiredMods) + { + Size = new Vector2(icon_height), + TooltipType = DifficultyIconTooltipType.Extended, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, }; } else @@ -329,7 +345,7 @@ namespace osu.Game.Screens.OnlinePlay protected override Drawable CreateContent() { - Action fontParameters = s => s.Font = OsuFont.Default.With(weight: FontWeight.SemiBold); + Action fontParameters = s => s.Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold); return maskingContainer = new Container { @@ -364,12 +380,15 @@ namespace osu.Game.Screens.OnlinePlay { new Drawable[] { - difficultyIconContainer = new Container + difficultyIconContainer = new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Left = 8, Right = 8 }, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Margin = new MarginPadding { Right = 8 }, }, mainFillFlow = new MainFlow(() => SelectedItem.Value == Model || !AllowSelection) { @@ -484,6 +503,20 @@ namespace osu.Game.Screens.OnlinePlay }, }; + protected override bool OnHover(HoverEvent e) + { + if (thumbnail != null) + thumbnail.Dimmed.Value = true; + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + if (thumbnail != null) + thumbnail.Dimmed.Value = false; + base.OnHoverLost(e); + } + protected override bool OnClick(ClickEvent e) { if (AllowSelection && valid.Value) From 1e2cac3e92f3cec02d28639a1314db80ae0bf022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 May 2024 11:44:55 +0200 Subject: [PATCH 575/581] Remove unused using directive --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index b28269c6e6..090236d6e2 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -33,7 +33,6 @@ using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; From 1f41261fc7d5e0c0f5a77cc064a1ba3c1d525ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 May 2024 12:01:02 +0200 Subject: [PATCH 576/581] Fix test failure --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 6446ebd35f..bd62a8b131 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -16,6 +16,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; @@ -317,13 +318,13 @@ namespace osu.Game.Tests.Visual.Multiplayer p.RequestResults = _ => resultsRequested = true; }); + AddUntilStep("wait for load", () => playlist.ChildrenOfType().Any() && playlist.ChildrenOfType().First().DrawWidth > 0); AddStep("move mouse to first item title", () => { var drawQuad = playlist.ChildrenOfType().First().ScreenSpaceDrawQuad; var location = (drawQuad.TopLeft + drawQuad.BottomLeft) / 2 + new Vector2(drawQuad.Width * 0.2f, 0); InputManager.MoveMouseTo(location); }); - AddUntilStep("wait for text load", () => playlist.ChildrenOfType().Any()); AddAssert("first item title not hovered", () => playlist.ChildrenOfType().First().IsHovered, () => Is.False); AddStep("click left mouse", () => InputManager.Click(MouseButton.Left)); AddUntilStep("first item selected", () => playlist.ChildrenOfType().First().IsSelectedItem, () => Is.True); From d97622491297efa4ee4699b06748588b2ea84a07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 19:59:25 +0900 Subject: [PATCH 577/581] Standardise padding on both sides of difficulty icon --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 090236d6e2..72866d1694 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -387,7 +387,7 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Y, Direction = FillDirection.Horizontal, Spacing = new Vector2(4), - Margin = new MarginPadding { Right = 8 }, + Margin = new MarginPadding { Right = 4 }, }, mainFillFlow = new MainFlow(() => SelectedItem.Value == Model || !AllowSelection) { From 75d961e6f2cc74b631a1980be91f3525e4551b80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 20:03:46 +0900 Subject: [PATCH 578/581] Pass the same thing in twice for better maybe --- .../Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs | 2 +- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 2 +- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs | 2 +- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs | 4 ++-- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs index f44fe2b90c..f5f9d121cc 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Beatmaps var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); beatmapSet.OnlineID = 241526; // ID hardcoded to ensure that the preview track exists online. - Child = thumbnail = new BeatmapCardThumbnail(beatmapSet) + Child = thumbnail = new BeatmapCardThumbnail(beatmapSet, beatmapSet) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 175c15ea7b..2c2761ff0c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -61,7 +61,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - thumbnail = new BeatmapCardThumbnail(BeatmapSet) + thumbnail = new BeatmapCardThumbnail(BeatmapSet, BeatmapSet) { Name = @"Left (icon) area", Size = new Vector2(height), diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs index 18e1584a98..c6ba4f234a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs @@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - thumbnail = new BeatmapCardThumbnail(BeatmapSet) + thumbnail = new BeatmapCardThumbnail(BeatmapSet, BeatmapSet) { Name = @"Left (icon) area", Size = new Vector2(height), diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index e70d115715..5d2717a787 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -35,14 +35,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public BeatmapCardThumbnail(IBeatmapSetInfo beatmapSetInfo) + public BeatmapCardThumbnail(IBeatmapSetInfo beatmapSetInfo, IBeatmapSetOnlineInfo onlineInfo) { InternalChildren = new Drawable[] { new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both, - OnlineInfo = beatmapSetInfo as IBeatmapSetOnlineInfo + OnlineInfo = onlineInfo }, background = new Box { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 72866d1694..e9126a1404 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -286,7 +286,7 @@ namespace osu.Game.Screens.OnlinePlay { difficultyIconContainer.Children = new Drawable[] { - thumbnail = new BeatmapCardThumbnail(beatmap.BeatmapSet!) + thumbnail = new BeatmapCardThumbnail(beatmap.BeatmapSet!, (IBeatmapSetOnlineInfo)beatmap.BeatmapSet!) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From aad0982e26ba3fe57caf1df7999d7d582c413097 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 May 2024 20:33:24 +0900 Subject: [PATCH 579/581] Adjust size of play button / progress to match `BeatmapCardThumbnail` usage --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index 5d2717a787..7b668d7dc4 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -61,7 +61,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(50), InnerRadius = 0.2f }, content = new Container @@ -92,6 +91,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards { base.Update(); progress.Progress = playButton.Progress.Value; + + playButton.Scale = new Vector2(DrawWidth / 100); + progress.Size = new Vector2(50 * DrawWidth / 100); } private void updateState() From 405c72c0d66ef895cbb5d34aa6c32e5d81dd5d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 May 2024 13:36:44 +0200 Subject: [PATCH 580/581] Fix mod display not being aligned with mapper text --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index e9126a1404..ab32ca2558 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -416,6 +416,8 @@ namespace osu.Game.Screens.OnlinePlay new FillFlowContainer { AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal, Spacing = new Vector2(10f, 0), Children = new Drawable[] @@ -438,7 +440,8 @@ namespace osu.Game.Screens.OnlinePlay Child = modDisplay = new ModDisplay { Scale = new Vector2(0.4f), - ExpansionMode = ExpansionMode.AlwaysExpanded + ExpansionMode = ExpansionMode.AlwaysExpanded, + Margin = new MarginPadding { Vertical = -6 }, } } } From c2e7cdfdccf1aaa83c5e9ab78cdf778547580951 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 May 2024 21:29:29 +0900 Subject: [PATCH 581/581] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1f241c6db5..3a20dd2fdb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index eba9abd3b8..2f64fcefa5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - +