From cc4c5f72d893bc014d4172b1e2ba1c0930165aa4 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sat, 20 Feb 2021 21:48:31 +0100 Subject: [PATCH 01/31] Move logic to keep selection in bounds into it's own method --- .../Edit/OsuSelectionHandler.cs | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 871339ae7b..51e0e80e30 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -211,26 +211,35 @@ namespace osu.Game.Rulesets.Osu.Edit { var hitObjects = selectedMovableObjects; - Quad quad = getSurroundingQuad(hitObjects); - - Vector2 newTopLeft = quad.TopLeft + delta; - if (newTopLeft.X < 0) - delta.X -= newTopLeft.X; - if (newTopLeft.Y < 0) - delta.Y -= newTopLeft.Y; - - Vector2 newBottomRight = quad.BottomRight + delta; - if (newBottomRight.X > DrawWidth) - delta.X -= newBottomRight.X - DrawWidth; - if (newBottomRight.Y > DrawHeight) - delta.Y -= newBottomRight.Y - DrawHeight; - foreach (var h in hitObjects) h.Position += delta; + moveSelectionInBounds(); + return true; } + private void moveSelectionInBounds() + { + var hitObjects = selectedMovableObjects; + + Quad quad = getSurroundingQuad(hitObjects); + Vector2 delta = new Vector2(0); + + if (quad.TopLeft.X < 0) + delta.X -= quad.TopLeft.X; + if (quad.TopLeft.Y < 0) + delta.Y -= quad.TopLeft.Y; + + if (quad.BottomRight.X > DrawWidth) + delta.X -= quad.BottomRight.X - DrawWidth; + if (quad.BottomRight.Y > DrawHeight) + delta.Y -= quad.BottomRight.Y - DrawHeight; + + foreach (var h in hitObjects) + h.Position += delta; + } + /// /// Returns a gamefield-space quad surrounding the provided hit objects. /// From 0b8009938a4c6c84b406cf6fd15ccd76e150935f Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sat, 20 Feb 2021 21:50:30 +0100 Subject: [PATCH 02/31] Prevent selection from breaking playfield bounds when scaling --- .../Edit/OsuSelectionHandler.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 51e0e80e30..6c62bdd922 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -135,14 +135,21 @@ namespace osu.Game.Rulesets.Osu.Edit adjustScaleFromAnchor(ref scale, reference); var hitObjects = selectedMovableObjects; + Quad selectionQuad = getSurroundingQuad(hitObjects); + + float newWidth = selectionQuad.Width + scale.X; + float newHeight = selectionQuad.Height + scale.Y; + + if ((newHeight > DrawHeight) || (newWidth > DrawWidth)) + return false; // 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) { - Quad quad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); - Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / quad.Width, 1 + scale.Y / quad.Height); + Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); foreach (var point in slider.Path.ControlPoints) point.Position.Value *= pathRelativeDeltaScale; @@ -153,23 +160,23 @@ namespace osu.Game.Rulesets.Osu.Edit if ((reference & Anchor.x0) > 0 && !moveSelection(new Vector2(-scale.X, 0))) return false; if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false; - Quad quad = getSurroundingQuad(hitObjects); - foreach (var h in hitObjects) { var newPosition = h.Position; // guard against no-ops and NaN. - if (scale.X != 0 && quad.Width > 0) - newPosition.X = quad.TopLeft.X + (h.X - quad.TopLeft.X) / quad.Width * (quad.Width + scale.X); + if (scale.X != 0 && selectionQuad.Width > 0) + newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - if (scale.Y != 0 && quad.Height > 0) - newPosition.Y = quad.TopLeft.Y + (h.Y - quad.TopLeft.Y) / quad.Height * (quad.Height + scale.Y); + if (scale.Y != 0 && selectionQuad.Height > 0) + newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); h.Position = newPosition; } } + moveSelectionInBounds(); + return true; } From 562a4cefdb952b54d3b6c20eda47e13b78378010 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sun, 21 Feb 2021 12:12:32 +0100 Subject: [PATCH 03/31] Simplify HandleScale by extracting methods --- .../Edit/OsuSelectionHandler.cs | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 6c62bdd922..9418752745 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -143,41 +143,18 @@ namespace osu.Game.Rulesets.Osu.Edit if ((newHeight > DrawHeight) || (newWidth > DrawWidth)) return false; + bool result; // 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) - { - Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); - Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); - - foreach (var point in slider.Path.ControlPoints) - point.Position.Value *= pathRelativeDeltaScale; - } + result = scaleSlider(slider, scale); else - { - // move the selection before scaling if dragging from top or left anchors. - if ((reference & Anchor.x0) > 0 && !moveSelection(new Vector2(-scale.X, 0))) return false; - if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false; - - foreach (var h in hitObjects) - { - var newPosition = h.Position; - - // guard against no-ops and NaN. - if (scale.X != 0 && selectionQuad.Width > 0) - newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - - if (scale.Y != 0 && selectionQuad.Height > 0) - newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); - - h.Position = newPosition; - } - } + result = scaleHitObjects(hitObjects, reference, scale); moveSelectionInBounds(); - return true; + return result; } private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) @@ -214,6 +191,42 @@ namespace osu.Game.Rulesets.Osu.Edit return true; } + private bool scaleSlider(Slider slider, Vector2 scale) + { + Quad quad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / quad.Width, 1 + scale.Y / quad.Height); + + foreach (var point in slider.Path.ControlPoints) + point.Position.Value *= pathRelativeDeltaScale; + + return true; + } + + private bool scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) + { + // move the selection before scaling if dragging from top or left anchors. + if ((reference & Anchor.x0) > 0 && !moveSelection(new Vector2(-scale.X, 0))) return false; + if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false; + + Quad selectionQuad = getSurroundingQuad(hitObjects); + + foreach (var h in hitObjects) + { + var newPosition = h.Position; + + // guard against no-ops and NaN. + if (scale.X != 0 && selectionQuad.Width > 0) + newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); + + if (scale.Y != 0 && selectionQuad.Height > 0) + newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); + + h.Position = newPosition; + } + + return true; + } + private bool moveSelection(Vector2 delta) { var hitObjects = selectedMovableObjects; From 2c6f92d12fb2d2a55ad8bb9f28e4f32634aadf8b Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sun, 21 Feb 2021 17:38:50 +0100 Subject: [PATCH 04/31] Move bounds check from moveSelection to HandleMovement --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 9418752745..9ca8404a26 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -35,8 +35,12 @@ namespace osu.Game.Rulesets.Osu.Edit referenceOrigin = null; } - public override bool HandleMovement(MoveSelectionEvent moveEvent) => - moveSelection(moveEvent.InstantDelta); + public override bool HandleMovement(MoveSelectionEvent moveEvent) + { + bool result = moveSelection(moveEvent.InstantDelta); + moveSelectionInBounds(); + return result; + } /// /// During a transform, the initial origin is stored so it can be used throughout the operation. @@ -234,8 +238,6 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var h in hitObjects) h.Position += delta; - moveSelectionInBounds(); - return true; } From 33985d9e7c27a3d2ec58e5a2cd6f3068e50be7ab Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sun, 21 Feb 2021 17:40:57 +0100 Subject: [PATCH 05/31] Rewrite scaling bounds check to behave more intuively --- .../Edit/OsuSelectionHandler.cs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 9ca8404a26..64dbe20c58 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -139,15 +139,8 @@ namespace osu.Game.Rulesets.Osu.Edit adjustScaleFromAnchor(ref scale, reference); var hitObjects = selectedMovableObjects; - Quad selectionQuad = getSurroundingQuad(hitObjects); - - float newWidth = selectionQuad.Width + scale.X; - float newHeight = selectionQuad.Height + scale.Y; - - if ((newHeight > DrawHeight) || (newWidth > DrawWidth)) - return false; - bool result; + // 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. @@ -197,8 +190,18 @@ namespace osu.Game.Rulesets.Osu.Edit private bool scaleSlider(Slider slider, Vector2 scale) { - Quad quad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); - Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / quad.Width, 1 + scale.Y / quad.Height); + Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); + + Quad selectionQuad = getSurroundingQuad(new OsuHitObject[] { slider }); + Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); + (bool X, bool Y) inBounds = isQuadInBounds(scaledQuad); + + if (!inBounds.X) + pathRelativeDeltaScale.X = 1; + + if (!inBounds.Y) + pathRelativeDeltaScale.Y = 1; foreach (var point in slider.Path.ControlPoints) point.Position.Value *= pathRelativeDeltaScale; @@ -213,16 +216,18 @@ namespace osu.Game.Rulesets.Osu.Edit if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false; Quad selectionQuad = getSurroundingQuad(hitObjects); + Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); + (bool X, bool Y) inBounds = isQuadInBounds(scaledQuad); foreach (var h in hitObjects) { var newPosition = h.Position; // guard against no-ops and NaN. - if (scale.X != 0 && selectionQuad.Width > 0) + if (scale.X != 0 && selectionQuad.Width > 0 && inBounds.X) newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - if (scale.Y != 0 && selectionQuad.Height > 0) + if (scale.Y != 0 && selectionQuad.Height > 0 && inBounds.Y) newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); h.Position = newPosition; @@ -231,6 +236,16 @@ namespace osu.Game.Rulesets.Osu.Edit return true; } + private (bool X, bool Y) isQuadInBounds(Quad quad) + { + (bool X, bool Y) result; + + result.X = (quad.TopLeft.X >= 0) && (quad.BottomRight.X < DrawWidth); + result.Y = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y < DrawHeight); + + return result; + } + private bool moveSelection(Vector2 delta) { var hitObjects = selectedMovableObjects; From 3491021f72a00eb5f4471dd6828678d2e6017315 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Feb 2021 20:58:46 +0100 Subject: [PATCH 06/31] Move moveSelection into HandleMovement --- .../Edit/OsuSelectionHandler.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 64dbe20c58..03c1676982 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -37,9 +37,13 @@ namespace osu.Game.Rulesets.Osu.Edit public override bool HandleMovement(MoveSelectionEvent moveEvent) { - bool result = moveSelection(moveEvent.InstantDelta); + var hitObjects = selectedMovableObjects; + + foreach (var h in hitObjects) + h.Position += moveEvent.InstantDelta; + moveSelectionInBounds(); - return result; + return true; } /// @@ -246,16 +250,6 @@ namespace osu.Game.Rulesets.Osu.Edit return result; } - private bool moveSelection(Vector2 delta) - { - var hitObjects = selectedMovableObjects; - - foreach (var h in hitObjects) - h.Position += delta; - - return true; - } - private void moveSelectionInBounds() { var hitObjects = selectedMovableObjects; From 71b30bdbbb7de4988ca31afe8110a477f98ab4b2 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Feb 2021 00:16:35 +0100 Subject: [PATCH 07/31] Adjust tuple usage --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 03c1676982..e1ee0612fa 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -199,12 +199,12 @@ namespace osu.Game.Rulesets.Osu.Edit Quad selectionQuad = getSurroundingQuad(new OsuHitObject[] { slider }); Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); - (bool X, bool Y) inBounds = isQuadInBounds(scaledQuad); + (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); - if (!inBounds.X) + if (!xInBounds) pathRelativeDeltaScale.X = 1; - if (!inBounds.Y) + if (!yInBounds) pathRelativeDeltaScale.Y = 1; foreach (var point in slider.Path.ControlPoints) @@ -221,17 +221,17 @@ namespace osu.Game.Rulesets.Osu.Edit Quad selectionQuad = getSurroundingQuad(hitObjects); Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); - (bool X, bool Y) inBounds = isQuadInBounds(scaledQuad); + (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); foreach (var h in hitObjects) { var newPosition = h.Position; // guard against no-ops and NaN. - if (scale.X != 0 && selectionQuad.Width > 0 && inBounds.X) + if (scale.X != 0 && selectionQuad.Width > 0 && xInBounds) newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - if (scale.Y != 0 && selectionQuad.Height > 0 && inBounds.Y) + if (scale.Y != 0 && selectionQuad.Height > 0 && yInBounds) newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); h.Position = newPosition; From 2a4139a2070df2f324526c651f9844fa58be1288 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Feb 2021 00:25:40 +0100 Subject: [PATCH 08/31] Refactor isQuadInBounds --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index e1ee0612fa..ede756ab47 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -242,12 +242,10 @@ namespace osu.Game.Rulesets.Osu.Edit private (bool X, bool Y) isQuadInBounds(Quad quad) { - (bool X, bool Y) result; + bool xInBounds = (quad.TopLeft.X >= 0) && (quad.BottomRight.X < DrawWidth); + bool yInBounds = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y < DrawHeight); - result.X = (quad.TopLeft.X >= 0) && (quad.BottomRight.X < DrawWidth); - result.Y = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y < DrawHeight); - - return result; + return (xInBounds, yInBounds); } private void moveSelectionInBounds() From 877e19421bfd44426bfcb15956fd75cc2dfabea0 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Feb 2021 16:36:51 +0100 Subject: [PATCH 09/31] Refactor movement while scaling --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index ede756ab47..28e6347f4f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -216,11 +216,11 @@ namespace osu.Game.Rulesets.Osu.Edit private bool scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) { // move the selection before scaling if dragging from top or left anchors. - if ((reference & Anchor.x0) > 0 && !moveSelection(new Vector2(-scale.X, 0))) return false; - if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false; + float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; + float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; Quad selectionQuad = getSurroundingQuad(hitObjects); - Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); + Quad scaledQuad = new Quad(selectionQuad.TopLeft.X + xOffset, selectionQuad.TopLeft.Y + yOffset, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); foreach (var h in hitObjects) @@ -229,10 +229,10 @@ namespace osu.Game.Rulesets.Osu.Edit // guard against no-ops and NaN. if (scale.X != 0 && selectionQuad.Width > 0 && xInBounds) - newPosition.X = selectionQuad.TopLeft.X + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); + newPosition.X = selectionQuad.TopLeft.X + xOffset + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); if (scale.Y != 0 && selectionQuad.Height > 0 && yInBounds) - newPosition.Y = selectionQuad.TopLeft.Y + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); + newPosition.Y = selectionQuad.TopLeft.Y + yOffset + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); h.Position = newPosition; } From def0e5c42e90e5e8eb984c794a31eee0567caee4 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Mar 2021 17:21:42 +0100 Subject: [PATCH 10/31] Fix off-by-one error in isQuadInBounds --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 28e6347f4f..0418aa1925 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -242,8 +242,8 @@ namespace osu.Game.Rulesets.Osu.Edit 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); + bool xInBounds = (quad.TopLeft.X >= 0) && (quad.BottomRight.X <= DrawWidth); + bool yInBounds = (quad.TopLeft.Y >= 0) && (quad.BottomRight.Y <= DrawHeight); return (xInBounds, yInBounds); } From 3d471d239f2007c05e7af3bf417bae1c89b1d910 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Mar 2021 12:41:43 +0100 Subject: [PATCH 11/31] Clamp multi-object scale instead of cancelling it --- .../Edit/OsuSelectionHandler.cs | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 0418aa1925..595357ee65 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -215,23 +215,23 @@ namespace osu.Game.Rulesets.Osu.Edit private bool scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) { + scale = getClampedScale(hitObjects, reference, scale); + // move the selection before scaling if dragging from top or left anchors. float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; Quad selectionQuad = getSurroundingQuad(hitObjects); - Quad scaledQuad = new Quad(selectionQuad.TopLeft.X + xOffset, selectionQuad.TopLeft.Y + yOffset, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); - (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); foreach (var h in hitObjects) { var newPosition = h.Position; // guard against no-ops and NaN. - if (scale.X != 0 && selectionQuad.Width > 0 && xInBounds) + if (scale.X != 0 && selectionQuad.Width > 0) newPosition.X = selectionQuad.TopLeft.X + xOffset + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X); - if (scale.Y != 0 && selectionQuad.Height > 0 && yInBounds) + if (scale.Y != 0 && selectionQuad.Height > 0) newPosition.Y = selectionQuad.TopLeft.Y + yOffset + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y); h.Position = newPosition; @@ -269,6 +269,43 @@ namespace osu.Game.Rulesets.Osu.Edit h.Position += delta; } + /// + /// Clamp scale 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 = getSurroundingQuad(hitObjects); + + //todo: this is not always correct for selections involving sliders + 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; + } + /// /// Returns a gamefield-space quad surrounding the provided hit objects. /// From e67ab3cca7cb55383a82769499686a37e09f82e6 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Mar 2021 16:09:44 +0100 Subject: [PATCH 12/31] Change single slider scaling to a method that works --- .../Edit/OsuSelectionHandler.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 595357ee65..ae92b12fd2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -197,19 +197,19 @@ namespace osu.Game.Rulesets.Osu.Edit Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); - Quad selectionQuad = getSurroundingQuad(new OsuHitObject[] { slider }); - Quad scaledQuad = new Quad(selectionQuad.TopLeft.X, selectionQuad.TopLeft.Y, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); - (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); - - if (!xInBounds) - pathRelativeDeltaScale.X = 1; - - if (!yInBounds) - pathRelativeDeltaScale.Y = 1; - foreach (var point in slider.Path.ControlPoints) point.Position.Value *= pathRelativeDeltaScale; + //if sliderhead or sliderend end up outside playfield, revert scaling. + Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); + (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); + + if (xInBounds && yInBounds) + return true; + + foreach (var point in slider.Path.ControlPoints) + point.Position.Value *= new Vector2(1 / pathRelativeDeltaScale.X, 1 / pathRelativeDeltaScale.Y); + return true; } From 013ddc734c09c110b9590b71cd1f04aa1a5bf021 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:04:35 +0900 Subject: [PATCH 13/31] Fix osu!catch fruit showing on plate when hidden mod is enabled Closes https://github.com/ppy/osu/issues/12065. --- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 13 ++++++++++++- osu.Game.Rulesets.Catch/UI/Catcher.cs | 5 +++-- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 7 ++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 4b008d2734..5c5e41234d 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -3,13 +3,16 @@ using System.Linq; using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModHidden : ModHidden + public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset { public override string Description => @"Play with fading fruits."; public override double ScoreMultiplier => 1.06; @@ -17,6 +20,14 @@ namespace osu.Game.Rulesets.Catch.Mods private const double fade_out_offset_multiplier = 0.6; private const double fade_out_duration_multiplier = 0.44; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + var drawableCatchRuleset = (DrawableCatchRuleset)drawableRuleset; + var catchPlayfield = (CatchPlayfield)drawableCatchRuleset.Playfield; + + catchPlayfield.CatcherArea.CatchFruitOnPlate = false; + } + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { base.ApplyNormalVisibilityState(hitObject, state); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ed875e7002..42be745c2e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition <= catcherPosition + halfCatchWidth; } - public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result) + public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result, bool placeOnPlate) { var catchResult = (CatchJudgementResult)result; catchResult.CatcherAnimationState = CurrentState; @@ -237,7 +237,8 @@ namespace osu.Game.Rulesets.Catch.UI { var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2); - placeCaughtObject(palpableObject, positionInStack); + if (placeOnPlate) + placeCaughtObject(palpableObject, positionInStack); if (hitLighting.Value) addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 44adbd5512..29c95ec61c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -21,6 +21,11 @@ namespace osu.Game.Rulesets.Catch.UI public readonly Catcher MovableCatcher; private readonly CatchComboDisplay comboDisplay; + /// + /// Whether fruit should appear on the plate. + /// + public bool CatchFruitOnPlate { get; set; } = true; + public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); @@ -41,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) { - MovableCatcher.OnNewResult(hitObject, result); + MovableCatcher.OnNewResult(hitObject, result, CatchFruitOnPlate); if (!result.Type.IsScorable()) return; From 6808719efabe9cd5997e51f916ecd9f56535b1f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:05:28 +0900 Subject: [PATCH 14/31] Update test scene --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 48efd73222..ddb6194899 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Catch.Tests Add(drawableObject); drawableObject.OnLoadComplete += _ => { - catcher.OnNewResult(drawableObject, result); + catcher.OnNewResult(drawableObject, result, true); drawableObject.Expire(); }; } From f6647de7693de11ad68b8316eb17483bd9bb3126 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 19:56:26 +0900 Subject: [PATCH 15/31] Add support for nudging objects in the editor using ctrl+arrow keys Closes #12042. --- .../Components/ComposeBlueprintContainer.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 79f457c050..5ab557804e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -19,6 +20,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components.TernaryButtons; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { @@ -70,6 +72,50 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.ControlPressed) + { + switch (e.Key) + { + case Key.Left: + moveSelection(new Vector2(-1, 0)); + return true; + + case Key.Right: + moveSelection(new Vector2(1, 0)); + return true; + + case Key.Up: + moveSelection(new Vector2(0, -1)); + return true; + + case Key.Down: + moveSelection(new Vector2(0, 1)); + return true; + } + } + + return false; + } + + /// + /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). + /// + /// + private void moveSelection(Vector2 delta) + { + var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault(); + + if (firstBlueprint == null) + return; + + // convert to game space coordinates + delta = firstBlueprint.ToScreenSpace(delta) - firstBlueprint.ToScreenSpace(Vector2.Zero); + + SelectionHandler.HandleMovement(new MoveSelectionEvent(firstBlueprint, firstBlueprint.ScreenSpaceSelectionPoint + delta)); + } + private void updatePlacementNewCombo() { if (currentPlacement?.HitObject is IHasComboInformation c) From 5d272bef9778d590b84e1e97edfde3d64a8b68de Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Fri, 26 Mar 2021 16:28:04 +0100 Subject: [PATCH 16/31] Remember ContolPoint positions instead of recalculating them --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index ae92b12fd2..b9578b4ae9 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -197,8 +197,13 @@ namespace osu.Game.Rulesets.Osu.Edit Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); + Queue oldControlPoints = new Queue(); + foreach (var point in slider.Path.ControlPoints) + { + oldControlPoints.Enqueue(point.Position.Value); point.Position.Value *= pathRelativeDeltaScale; + } //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); @@ -207,8 +212,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (xInBounds && yInBounds) return true; - foreach (var point in slider.Path.ControlPoints) - point.Position.Value *= new Vector2(1 / pathRelativeDeltaScale.X, 1 / pathRelativeDeltaScale.Y); + foreach(var point in slider.Path.ControlPoints) + point.Position.Value = oldControlPoints.Dequeue(); return true; } From 25ea60cb92c6d4e40b7c442d68d2fd17b4a7056d Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Fri, 26 Mar 2021 16:39:13 +0100 Subject: [PATCH 17/31] Remove return values from HandleScale submethods --- .../Edit/OsuSelectionHandler.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index b9578b4ae9..8a657d0f9b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -143,19 +143,18 @@ namespace osu.Game.Rulesets.Osu.Edit adjustScaleFromAnchor(ref scale, reference); var hitObjects = selectedMovableObjects; - bool result; // 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) - result = scaleSlider(slider, scale); + scaleSlider(slider, scale); else - result = scaleHitObjects(hitObjects, reference, scale); + scaleHitObjects(hitObjects, reference, scale); moveSelectionInBounds(); - return result; + return true; } private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) @@ -192,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Edit return true; } - private bool scaleSlider(Slider slider, Vector2 scale) + private void scaleSlider(Slider slider, Vector2 scale) { Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); @@ -210,15 +209,13 @@ namespace osu.Game.Rulesets.Osu.Edit (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); if (xInBounds && yInBounds) - return true; + return; foreach(var point in slider.Path.ControlPoints) point.Position.Value = oldControlPoints.Dequeue(); - - return true; } - private bool scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) + private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) { scale = getClampedScale(hitObjects, reference, scale); @@ -241,8 +238,6 @@ namespace osu.Game.Rulesets.Osu.Edit h.Position = newPosition; } - - return true; } private (bool X, bool Y) isQuadInBounds(Quad quad) From 305c2e31cfc7f7e6e30f94d561415619b37c87e0 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Fri, 26 Mar 2021 16:45:05 +0100 Subject: [PATCH 18/31] Clarify todo comment --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 8a657d0f9b..24b0d22c1a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -283,7 +283,7 @@ namespace osu.Game.Rulesets.Osu.Edit Quad selectionQuad = getSurroundingQuad(hitObjects); - //todo: this is not always correct for selections involving sliders + //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 From a50c4be8ab806e4f8875cd286a01d6cf5a3a2a7f Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Fri, 26 Mar 2021 17:41:36 +0100 Subject: [PATCH 19/31] Add missing space --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 24b0d22c1a..3a4a37c8cc 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (xInBounds && yInBounds) return; - foreach(var point in slider.Path.ControlPoints) + foreach (var point in slider.Path.ControlPoints) point.Position.Value = oldControlPoints.Dequeue(); } From 1d99a63f17f8e339097552dc51fbc27bef00b5c7 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Mon, 29 Mar 2021 14:02:53 +0200 Subject: [PATCH 20/31] Limit minimum size for single slider scaling --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 3a4a37c8cc..6350bd7190 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -194,6 +194,10 @@ namespace osu.Game.Rulesets.Osu.Edit private void scaleSlider(Slider slider, Vector2 scale) { Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + + // 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(1 + scale.X / sliderQuad.Width, 1 + scale.Y / sliderQuad.Height); Queue oldControlPoints = new Queue(); From 17b16d4f8914b9989b2f116e334e26fd0a4c39fe Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Mon, 29 Mar 2021 14:17:30 +0200 Subject: [PATCH 21/31] Clarify purpose of getClampedScale() --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 6350bd7190..07643bd75e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Osu.Edit } /// - /// Clamp scale where selection does not exceed playfield bounds or flip. + /// 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 From badf5ee4a2403998b4508907425630b21ea9e392 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 29 Mar 2021 15:03:10 +0200 Subject: [PATCH 22/31] Fix stable.json file directory location due to the change of how TournamentStorage works --- .../NonVisual/IPCLocationTest.cs | 68 +++++++++++++++++++ osu.Game.Tournament/IO/TournamentStorage.cs | 21 +++--- osu.Game.Tournament/Models/StableInfo.cs | 6 +- 3 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs new file mode 100644 index 0000000000..29586dfdec --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Tournament.IO; +using osu.Game.Tournament.IPC; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + [TestFixture] + public class IPCLocationTest + { + [Test] + public void CheckIPCLocation() + { + // don't use clean run because files are being written before osu! launches. + using (HeadlessGameHost host = new HeadlessGameHost(nameof(CheckIPCLocation))) + { + string basePath = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(CheckIPCLocation)); + + // Set up a fake IPC client for the IPC Storage to switch to. + string testCeDir = Path.Combine(basePath, "stable-ce"); + Directory.CreateDirectory(testCeDir); + + string ipcFile = Path.Combine(testCeDir, "ipc.txt"); + File.WriteAllText(ipcFile, string.Empty); + + try + { + var osu = loadOsu(host); + TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); + FileBasedIPC ipc = (FileBasedIPC)osu.Dependencies.Get(); + + Assert.True(ipc.SetIPCLocation(testCeDir)); + Assert.True(storage.AllTournaments.Exists("stable.json")); + } + finally + { + host.Storage.DeleteDirectory(testCeDir); + host.Storage.DeleteDirectory("tournaments"); + host.Exit(); + } + } + } + + private TournamentGameBase loadOsu(GameHost host) + { + var osu = new TournamentGameBase(); + Task.Run(() => host.Run(osu)); + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return osu; + } + + private static void waitForOrAssert(Func result, string failureMessage, int timeout = 90000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + } +} diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 5d9fed6288..044b60bbd5 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.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 System.Collections.Generic; +using System.IO; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.IO; -using System.IO; -using System.Collections.Generic; using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.IO @@ -15,7 +15,12 @@ namespace osu.Game.Tournament.IO { private const string default_tournament = "default"; private readonly Storage storage; - private readonly Storage allTournaments; + + /// + /// The storage where all tournaments are located. + /// + public readonly Storage AllTournaments; + private readonly TournamentStorageManager storageConfig; public readonly Bindable CurrentTournament; @@ -23,16 +28,16 @@ namespace osu.Game.Tournament.IO : base(storage.GetStorageForDirectory("tournaments"), string.Empty) { this.storage = storage; - allTournaments = UnderlyingStorage; + AllTournaments = UnderlyingStorage; storageConfig = new TournamentStorageManager(storage); if (storage.Exists("tournament.ini")) { - ChangeTargetStorage(allTournaments.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); + ChangeTargetStorage(AllTournaments.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); } else - Migrate(allTournaments.GetStorageForDirectory(default_tournament)); + Migrate(AllTournaments.GetStorageForDirectory(default_tournament)); CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); @@ -42,11 +47,11 @@ namespace osu.Game.Tournament.IO private void updateTournament(ValueChangedEvent newTournament) { - ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue)); + ChangeTargetStorage(AllTournaments.GetStorageForDirectory(newTournament.NewValue)); Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); } - public IEnumerable ListTournaments() => allTournaments.GetDirectories(string.Empty); + public IEnumerable ListTournaments() => AllTournaments.GetDirectories(string.Empty); public override void Migrate(Storage newStorage) { diff --git a/osu.Game.Tournament/Models/StableInfo.cs b/osu.Game.Tournament/Models/StableInfo.cs index 0b0050a245..d390f88d59 100644 --- a/osu.Game.Tournament/Models/StableInfo.cs +++ b/osu.Game.Tournament/Models/StableInfo.cs @@ -5,6 +5,7 @@ using System; using System.IO; using Newtonsoft.Json; using osu.Framework.Platform; +using osu.Game.Tournament.IO; namespace osu.Game.Tournament.Models { @@ -24,13 +25,14 @@ namespace osu.Game.Tournament.Models /// public event Action OnStableInfoSaved; - private const string config_path = "tournament/stable.json"; + private const string config_path = "stable.json"; private readonly Storage storage; public StableInfo(Storage storage) { - this.storage = storage; + TournamentStorage tStorage = (TournamentStorage)storage; + this.storage = tStorage.AllTournaments; if (!storage.Exists(config_path)) return; From 6c5a10a7449a24fdd6932c6cb8ba99c7d98affd6 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 29 Mar 2021 15:27:25 +0200 Subject: [PATCH 23/31] Add missing license header --- osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index 29586dfdec..086ed54435 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.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; using System.IO; using System.Threading; From 2d344ae6ffd9a5dee8e47096060a1c4f949b677e Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 29 Mar 2021 16:16:50 +0200 Subject: [PATCH 24/31] wait for IPC to be populated in the test Did not see this when locally running test until after a couple of subsequent runs. --- osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index 086ed54435..4791da93c6 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -38,6 +38,8 @@ namespace osu.Game.Tournament.Tests.NonVisual TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); FileBasedIPC ipc = (FileBasedIPC)osu.Dependencies.Get(); + waitForOrAssert(() => ipc != null, @"ipc could not be populated in a reasonable amount of time"); + Assert.True(ipc.SetIPCLocation(testCeDir)); Assert.True(storage.AllTournaments.Exists("stable.json")); } From b82247aabec2ee10c12105701d1c0e073a775e6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 14:13:16 +0900 Subject: [PATCH 25/31] Add inline comments and use Vector2.Zero --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 07643bd75e..82301bd37f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -39,9 +39,11 @@ namespace osu.Game.Rulesets.Osu.Edit { var hitObjects = selectedMovableObjects; + // this will potentially move the selection out of bounds... foreach (var h in hitObjects) h.Position += moveEvent.InstantDelta; + // but this will be corrected. moveSelectionInBounds(); return true; } @@ -153,7 +155,6 @@ namespace osu.Game.Rulesets.Osu.Edit scaleHitObjects(hitObjects, reference, scale); moveSelectionInBounds(); - return true; } @@ -257,7 +258,8 @@ namespace osu.Game.Rulesets.Osu.Edit var hitObjects = selectedMovableObjects; Quad quad = getSurroundingQuad(hitObjects); - Vector2 delta = new Vector2(0); + + Vector2 delta = Vector2.Zero; if (quad.TopLeft.X < 0) delta.X -= quad.TopLeft.X; From 88035f73e00229ca5587adb916dc36716da29fb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 14:23:46 +0900 Subject: [PATCH 26/31] Fix incorrect wait logic in IPC location test Not really willing to put more effort into fixing this one. Should do the job. --- .../NonVisual/IPCLocationTest.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs index 4791da93c6..4c5f5a7a1a 100644 --- a/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/IPCLocationTest.cs @@ -26,26 +26,26 @@ namespace osu.Game.Tournament.Tests.NonVisual string basePath = Path.Combine(RuntimeInfo.StartupDirectory, "headless", nameof(CheckIPCLocation)); // Set up a fake IPC client for the IPC Storage to switch to. - string testCeDir = Path.Combine(basePath, "stable-ce"); - Directory.CreateDirectory(testCeDir); + string testStableInstallDirectory = Path.Combine(basePath, "stable-ce"); + Directory.CreateDirectory(testStableInstallDirectory); - string ipcFile = Path.Combine(testCeDir, "ipc.txt"); + string ipcFile = Path.Combine(testStableInstallDirectory, "ipc.txt"); File.WriteAllText(ipcFile, string.Empty); try { var osu = loadOsu(host); TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get(); - FileBasedIPC ipc = (FileBasedIPC)osu.Dependencies.Get(); + FileBasedIPC ipc = null; - waitForOrAssert(() => ipc != null, @"ipc could not be populated in a reasonable amount of time"); + waitForOrAssert(() => (ipc = osu.Dependencies.Get() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time"); - Assert.True(ipc.SetIPCLocation(testCeDir)); + Assert.True(ipc.SetIPCLocation(testStableInstallDirectory)); Assert.True(storage.AllTournaments.Exists("stable.json")); } finally { - host.Storage.DeleteDirectory(testCeDir); + host.Storage.DeleteDirectory(testStableInstallDirectory); host.Storage.DeleteDirectory("tournaments"); host.Exit(); } From 89bea2868a9ca68a8c09a9c93db6f504e9022a96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 14:33:55 +0900 Subject: [PATCH 27/31] Move bool one level down --- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 9 +++++++-- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 7 +------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 5c5e41234d..bba42dea97 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Mods var drawableCatchRuleset = (DrawableCatchRuleset)drawableRuleset; var catchPlayfield = (CatchPlayfield)drawableCatchRuleset.Playfield; - catchPlayfield.CatcherArea.CatchFruitOnPlate = false; + catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false; } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 42be745c2e..5d57e84b75 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -43,6 +43,11 @@ namespace osu.Game.Rulesets.Catch.UI /// public bool HyperDashing => hyperDashModifier != 1; + /// + /// Whether fruit should appear on the plate. + /// + public bool CatchFruitOnPlate { get; set; } = true; + /// /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. /// @@ -223,7 +228,7 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition <= catcherPosition + halfCatchWidth; } - public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result, bool placeOnPlate) + public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result) { var catchResult = (CatchJudgementResult)result; catchResult.CatcherAnimationState = CurrentState; @@ -237,7 +242,7 @@ namespace osu.Game.Rulesets.Catch.UI { var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2); - if (placeOnPlate) + if (CatchFruitOnPlate) placeCaughtObject(palpableObject, positionInStack); if (hitLighting.Value) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 29c95ec61c..44adbd5512 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -21,11 +21,6 @@ namespace osu.Game.Rulesets.Catch.UI public readonly Catcher MovableCatcher; private readonly CatchComboDisplay comboDisplay; - /// - /// Whether fruit should appear on the plate. - /// - public bool CatchFruitOnPlate { get; set; } = true; - public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); @@ -46,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.UI public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) { - MovableCatcher.OnNewResult(hitObject, result, CatchFruitOnPlate); + MovableCatcher.OnNewResult(hitObject, result); if (!result.Type.IsScorable()) return; From f12353a99e5b38c57d1578460b542b27d07a1cec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 14:57:06 +0900 Subject: [PATCH 28/31] Update forgotten test scene usage --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index ddb6194899..48efd73222 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Catch.Tests Add(drawableObject); drawableObject.OnLoadComplete += _ => { - catcher.OnNewResult(drawableObject, result, true); + catcher.OnNewResult(drawableObject, result); drawableObject.Expire(); }; } From 1d968009c2c329a1abd107409c9e37c302ae36c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 16:07:04 +0900 Subject: [PATCH 29/31] Add osu!mania key filtering using "keys=4" at song select --- .../Beatmaps/ManiaBeatmapConverter.cs | 8 ++++- .../ManiaFilterCriteria.cs | 33 +++++++++++++++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 6 ++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 7a0e3b2b76..756207a201 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (IsForCurrentRuleset) { - TargetColumns = (int)Math.Max(1, roundedCircleSize); + TargetColumns = GetColumnCountForNonConvert(beatmap.BeatmapInfo); if (TargetColumns > ManiaRuleset.MAX_STAGE_KEYS) { @@ -71,6 +71,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps originalTargetColumns = TargetColumns; } + internal static int GetColumnCountForNonConvert(BeatmapInfo beatmap) + { + var roundedCircleSize = Math.Round(beatmap.BaseDifficulty.CircleSize); + return (int)Math.Max(1, roundedCircleSize); + } + public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition); protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs new file mode 100644 index 0000000000..c9a1ae84d4 --- /dev/null +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -0,0 +1,33 @@ +// 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.Beatmaps; +using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Filter; + +namespace osu.Game.Rulesets.Mania +{ + public class ManiaFilterCriteria : IRulesetFilterCriteria + { + private FilterCriteria.OptionalRange keys; + + public bool Matches(BeatmapInfo beatmap) + { + return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID) && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap)); + } + + public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) + { + switch (key) + { + case "key": + case "keys": + return FilterQueryParser.TryUpdateCriteriaRange(ref keys, op, value); + } + + return false; + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index d624e094ad..88b63606b9 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -22,6 +22,7 @@ using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Difficulty; @@ -382,6 +383,11 @@ namespace osu.Game.Rulesets.Mania } } }; + + public override IRulesetFilterCriteria CreateRulesetFilterCriteria() + { + return new ManiaFilterCriteria(); + } } public enum PlayfieldType From e769ef45be36d1bf700969bf76bcab5c0dac2f99 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 16:55:39 +0900 Subject: [PATCH 30/31] Fix misplaced parenthesis --- osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index c9a1ae84d4..d9a278ef29 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania public bool Matches(BeatmapInfo beatmap) { - return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID) && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap)); + return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap))); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) From 56428a027e53e9e89dcb321a5f58eb32c899d52d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 16:56:19 +0900 Subject: [PATCH 31/31] Change static method to public --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 756207a201..26393c8edb 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps originalTargetColumns = TargetColumns; } - internal static int GetColumnCountForNonConvert(BeatmapInfo beatmap) + public static int GetColumnCountForNonConvert(BeatmapInfo beatmap) { var roundedCircleSize = Math.Round(beatmap.BaseDifficulty.CircleSize); return (int)Math.Max(1, roundedCircleSize);