From cc4c5f72d893bc014d4172b1e2ba1c0930165aa4 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Sat, 20 Feb 2021 21:48:31 +0100 Subject: [PATCH 01/94] 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/94] 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/94] 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/94] 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/94] 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/94] 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/94] 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/94] 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/94] 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 115c186cb7a624065620391a83be90c502827bb2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Mar 2021 02:16:10 +0300 Subject: [PATCH 10/94] Move hit circle font from osu! ruleset --- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 2 -- osu.Game/Skinning/LegacySkinConfiguration.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 63c9b53278..75a62a6f8e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -5,8 +5,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { public enum OsuSkinConfiguration { - HitCirclePrefix, - HitCircleOverlap, SliderBorderSize, SliderPathRadius, AllowSliderBallTint, diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs index 84a834ec22..20d1da8aaa 100644 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ b/osu.Game/Skinning/LegacySkinConfiguration.cs @@ -19,6 +19,8 @@ namespace osu.Game.Skinning ComboOverlap, ScorePrefix, ScoreOverlap, + HitCirclePrefix, + HitCircleOverlap, AnimationFramerate, LayeredHitSounds } From 91741564e8e06926a36b49f1c15dba97a03564da Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Mar 2021 02:15:23 +0300 Subject: [PATCH 11/94] Add legacy font enum and extensions --- osu.Game/Skinning/LegacyFont.cs | 15 +++++++ osu.Game/Skinning/LegacySkinExtensions.cs | 48 ++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Skinning/LegacyFont.cs diff --git a/osu.Game/Skinning/LegacyFont.cs b/osu.Game/Skinning/LegacyFont.cs new file mode 100644 index 0000000000..d1971cb84c --- /dev/null +++ b/osu.Game/Skinning/LegacyFont.cs @@ -0,0 +1,15 @@ +// 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.Skinning +{ + /// + /// The type of legacy font to use for s. + /// + public enum LegacyFont + { + Score, + Combo, + HitCircle, + } +} diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index a7c084998d..d08f50bccb 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.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.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -63,8 +64,51 @@ namespace osu.Game.Skinning } } - public static bool HasFont(this ISkin source, string fontPrefix) - => source.GetTexture($"{fontPrefix}-0") != null; + public static bool HasFont(this ISkin source, LegacyFont font) + { + return source.GetTexture($"{source.GetFontPrefix(font)}-0") != null; + } + + public static string GetFontPrefix(this ISkin source, LegacyFont font) + { + switch (font) + { + case LegacyFont.Score: + return source.GetConfig(LegacySetting.ScorePrefix)?.Value ?? "score"; + + case LegacyFont.Combo: + return source.GetConfig(LegacySetting.ComboPrefix)?.Value ?? "score"; + + case LegacyFont.HitCircle: + return source.GetConfig(LegacySetting.HitCirclePrefix)?.Value ?? "default"; + + default: + throw new ArgumentOutOfRangeException(nameof(font)); + } + } + + /// + /// Returns the numeric overlap of number sprites to use. + /// A positive number will bring the number sprites closer together, while a negative number + /// will split them apart more. + /// + public static float GetFontOverlap(this ISkin source, LegacyFont font) + { + switch (font) + { + case LegacyFont.Score: + return source.GetConfig(LegacySetting.ScoreOverlap)?.Value ?? -2f; + + case LegacyFont.Combo: + return source.GetConfig(LegacySetting.ComboOverlap)?.Value ?? -2f; + + case LegacyFont.HitCircle: + return source.GetConfig(LegacySetting.HitCircleOverlap)?.Value ?? -2f; + + default: + throw new ArgumentOutOfRangeException(nameof(font)); + } + } public class SkinnableTextureAnimation : TextureAnimation { From 64d1cb519324df2663aa85502ba6bed305ae1b0f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Mar 2021 02:30:16 +0300 Subject: [PATCH 12/94] Remove text skin components in favour of plain `LegacySpriteText`s --- osu.Game/Skinning/HUDSkinComponents.cs | 2 -- osu.Game/Skinning/LegacySkin.cs | 21 +-------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/osu.Game/Skinning/HUDSkinComponents.cs b/osu.Game/Skinning/HUDSkinComponents.cs index b01be2d5a0..a345e060e5 100644 --- a/osu.Game/Skinning/HUDSkinComponents.cs +++ b/osu.Game/Skinning/HUDSkinComponents.cs @@ -9,7 +9,5 @@ namespace osu.Game.Skinning ScoreCounter, AccuracyCounter, HealthDisplay, - ScoreText, - ComboText, } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e5d0217671..83854c592b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -18,7 +18,6 @@ using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; -using osuTK; using osuTK.Graphics; namespace osu.Game.Skinning @@ -327,19 +326,13 @@ namespace osu.Game.Skinning return null; } - private string scorePrefix => GetConfig(LegacySkinConfiguration.LegacySetting.ScorePrefix)?.Value ?? "score"; - - private string comboPrefix => GetConfig(LegacySkinConfiguration.LegacySetting.ComboPrefix)?.Value ?? "score"; - - private bool hasScoreFont => this.HasFont(scorePrefix); - public override Drawable GetDrawableComponent(ISkinComponent component) { switch (component) { case HUDSkinComponent hudComponent: { - if (!hasScoreFont) + if (!this.HasFont(LegacyFont.Score)) return null; switch (hudComponent.Component) @@ -355,18 +348,6 @@ namespace osu.Game.Skinning case HUDSkinComponents.HealthDisplay: return new LegacyHealthDisplay(this); - - case HUDSkinComponents.ComboText: - return new LegacySpriteText(this, comboPrefix) - { - Spacing = new Vector2(-(GetConfig(LegacySkinConfiguration.LegacySetting.ComboOverlap)?.Value ?? -2), 0) - }; - - case HUDSkinComponents.ScoreText: - return new LegacySpriteText(this, scorePrefix) - { - Spacing = new Vector2(-(GetConfig(LegacySkinConfiguration.LegacySetting.ScoreOverlap)?.Value ?? -2), 0) - }; } return null; From 2a2ee3fa5ea86bbfc875f5659c01b3463adb93fb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Mar 2021 02:28:08 +0300 Subject: [PATCH 13/94] Update legacy sprite text constructor --- osu.Game/Skinning/LegacyRollingCounter.cs | 23 +++++------------------ osu.Game/Skinning/LegacySpriteText.cs | 7 +++++-- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/osu.Game/Skinning/LegacyRollingCounter.cs b/osu.Game/Skinning/LegacyRollingCounter.cs index 8aa9d4e9af..0261db0e64 100644 --- a/osu.Game/Skinning/LegacyRollingCounter.cs +++ b/osu.Game/Skinning/LegacyRollingCounter.cs @@ -4,7 +4,6 @@ using System; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osuTK; namespace osu.Game.Skinning { @@ -14,9 +13,7 @@ namespace osu.Game.Skinning public class LegacyRollingCounter : RollingCounter { private readonly ISkin skin; - - private readonly string fontName; - private readonly float fontOverlap; + private readonly LegacyFont font; protected override bool IsRollingProportional => true; @@ -24,17 +21,11 @@ namespace osu.Game.Skinning /// Creates a new . /// /// The from which to get counter number sprites. - /// The name of the legacy font to use. - /// - /// The numeric overlap of number sprites to use. - /// A positive number will bring the number sprites closer together, while a negative number - /// will split them apart more. - /// - public LegacyRollingCounter(ISkin skin, string fontName, float fontOverlap) + /// The legacy font to use for the counter. + public LegacyRollingCounter(ISkin skin, LegacyFont font) { this.skin = skin; - this.fontName = fontName; - this.fontOverlap = fontOverlap; + this.font = font; } protected override double GetProportionalDuration(int currentValue, int newValue) @@ -42,10 +33,6 @@ namespace osu.Game.Skinning return Math.Abs(newValue - currentValue) * 75.0; } - protected sealed override OsuSpriteText CreateSpriteText() => - new LegacySpriteText(skin, fontName) - { - Spacing = new Vector2(-fontOverlap, 0f) - }; + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, font); } } diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 5d0e312f7c..c55400e219 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using osu.Framework.Graphics.Sprites; using osu.Framework.Text; using osu.Game.Graphics.Sprites; +using osuTK; namespace osu.Game.Skinning { @@ -16,12 +17,14 @@ namespace osu.Game.Skinning protected override char[] FixedWidthExcludeCharacters => new[] { ',', '.', '%', 'x' }; - public LegacySpriteText(ISkin skin, string font = "score") + public LegacySpriteText(ISkin skin, LegacyFont font) { Shadow = false; UseFullGlyphHeight = false; - Font = new FontUsage(font, 1, fixedWidth: true); + Font = new FontUsage(skin.GetFontPrefix(font), 1, fixedWidth: true); + Spacing = new Vector2(-skin.GetFontOverlap(font), 0); + glyphStore = new LegacyGlyphStore(skin); } From 43c1e1d217388f56fe5e1e616fb000c65f7d2382 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 7 Mar 2021 02:18:31 +0300 Subject: [PATCH 14/94] Update existing usages Resolve post-conflict issues --- .../Legacy/CatchLegacySkinTransformer.cs | 4 +--- .../Legacy/LegacyCatchComboCounter.cs | 8 ++------ .../Legacy/OsuLegacySkinTransformer.cs | 17 +++++++---------- .../Screens/Play/HUD/LegacyComboCounter.cs | 19 ++++++++----------- osu.Game/Skinning/LegacyAccuracyCounter.cs | 8 +++++--- osu.Game/Skinning/LegacyScoreCounter.cs | 8 +++++--- 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 41fd0fe776..1b48832ed6 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -5,7 +5,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Skinning; using osuTK.Graphics; -using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { @@ -14,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy /// /// For simplicity, let's use legacy combo font texture existence as a way to identify legacy skins from default. /// - private bool providesComboCounter => this.HasFont(GetConfig(LegacySetting.ComboPrefix)?.Value ?? "score"); + private bool providesComboCounter => this.HasFont(LegacyFont.Combo); public CatchLegacySkinTransformer(ISkinSource source) : base(source) @@ -69,7 +68,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy this.GetAnimation("fruit-ryuuta", true, true, true); case CatchSkinComponents.CatchComboCounter: - if (providesComboCounter) return new LegacyCatchComboCounter(Source); diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs index f797ae75c2..28ee7bd813 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs @@ -7,7 +7,6 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; -using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { @@ -22,9 +21,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy public LegacyCatchComboCounter(ISkin skin) { - var fontName = skin.GetConfig(LegacySetting.ComboPrefix)?.Value ?? "score"; - var fontOverlap = skin.GetConfig(LegacySetting.ComboOverlap)?.Value ?? -2f; - AutoSizeAxes = Axes.Both; Alpha = 0f; @@ -34,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy InternalChildren = new Drawable[] { - explosion = new LegacyRollingCounter(skin, fontName, fontOverlap) + explosion = new LegacyRollingCounter(skin, LegacyFont.Combo) { Alpha = 0.65f, Blending = BlendingParameters.Additive, @@ -42,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy Origin = Anchor.Centre, Scale = new Vector2(1.5f), }, - counter = new LegacyRollingCounter(skin, fontName, fontOverlap) + counter = new LegacyRollingCounter(skin, LegacyFont.Combo) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index d74f885573..ffe238c507 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -97,17 +97,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.HitCircleText: - var font = GetConfig(OsuSkinConfiguration.HitCirclePrefix)?.Value ?? "default"; - var overlap = GetConfig(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? -2; + if (!this.HasFont(LegacyFont.HitCircle)) + return null; - return !this.HasFont(font) - ? null - : new LegacySpriteText(Source, font) - { - // stable applies a blanket 0.8x scale to hitcircle fonts - Scale = new Vector2(0.8f), - Spacing = new Vector2(-overlap, 0) - }; + return new LegacySpriteText(Source, LegacyFont.HitCircle) + { + // stable applies a blanket 0.8x scale to hitcircle fonts + Scale = new Vector2(0.8f), + }; case OsuSkinComponents.SpinnerBody: bool hasBackground = Source.GetTexture("spinner-background") != null; diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 81183a425a..b4604c0d01 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; using osu.Game.Skinning; using osuTK; @@ -84,16 +83,16 @@ namespace osu.Game.Screens.Play.HUD { InternalChildren = new[] { - popOutCount = createSpriteText().With(s => + popOutCount = new LegacySpriteText(skin, LegacyFont.Combo) { - s.Alpha = 0; - s.Margin = new MarginPadding(0.05f); - s.Blending = BlendingParameters.Additive; - }), - displayedCountSpriteText = createSpriteText().With(s => + Alpha = 0, + Margin = new MarginPadding(0.05f), + Blending = BlendingParameters.Additive, + }, + displayedCountSpriteText = new LegacySpriteText(skin, LegacyFont.Combo) { - s.Alpha = 0; - }) + Alpha = 0, + }, }; Current.ValueChanged += combo => updateCount(combo.NewValue == 0); @@ -247,7 +246,5 @@ namespace osu.Game.Screens.Play.HUD double difference = currentValue > newValue ? currentValue - newValue : newValue - currentValue; return difference * rolling_duration; } - - private OsuSpriteText createSpriteText() => (OsuSpriteText)skin.GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboText)); } } diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index 5eda374337..7d6f1dc916 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -29,9 +29,11 @@ namespace osu.Game.Skinning [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } - protected sealed override OsuSpriteText CreateSpriteText() - => (OsuSpriteText)skin?.GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreText)) - ?.With(s => s.Anchor = s.Origin = Anchor.TopRight); + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, LegacyFont.Score) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }; protected override void Update() { diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index 5bffeff5a8..1d330ef495 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.cs @@ -33,8 +33,10 @@ namespace osu.Game.Skinning Margin = new MarginPadding(10); } - protected sealed override OsuSpriteText CreateSpriteText() - => (OsuSpriteText)skin.GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreText)) - .With(s => s.Anchor = s.Origin = Anchor.TopRight); + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, LegacyFont.Score) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }; } } From 8046b5a818f8d2a69d7199e6cff6fa2a1db024d8 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 17 Mar 2021 17:35:49 +0800 Subject: [PATCH 15/94] set text to platform clipboard on copy --- osu.Game/Screens/Edit/Editor.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0ba202b082..88383bd3ed 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -21,6 +21,7 @@ using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; @@ -29,6 +30,7 @@ using osu.Game.IO.Serialization; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; @@ -60,6 +62,9 @@ namespace osu.Game.Screens.Edit protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; + [Resolved] + private GameHost host { get; set; } + [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -104,7 +109,7 @@ namespace osu.Game.Screens.Edit private MusicController music { get; set; } [BackgroundDependencyLoader] - private void load(OsuColour colours, GameHost host, OsuConfigManager config) + private void load(OsuColour colours, OsuConfigManager config) { if (Beatmap.Value is DummyWorkingBeatmap) { @@ -542,8 +547,12 @@ namespace osu.Game.Screens.Edit protected void Copy() { if (editorBeatmap.SelectedHitObjects.Count == 0) + { + host.GetClipboard()?.SetText($"{clock.CurrentTime.ToEditorFormattedString()} - "); return; + } + host.GetClipboard()?.SetText($"{editorBeatmap.SelectedHitObjects.FirstOrDefault().StartTime.ToEditorFormattedString()} ({string.Join(',', editorBeatmap.SelectedHitObjects.Select(h => ((h as IHasComboInformation)?.IndexInCurrentCombo + 1 ?? 0).ToString()))}) - "); clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); } From 133ff085a53711229834c0c3b3944f1c29d91b73 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 17 Mar 2021 18:06:40 +0800 Subject: [PATCH 16/94] refactor code --- osu.Game/Screens/Edit/Editor.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 88383bd3ed..fdb31a8b8c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Text; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -546,14 +547,24 @@ namespace osu.Game.Screens.Edit protected void Copy() { + var builder = new StringBuilder(); + const string suffix = " - "; + if (editorBeatmap.SelectedHitObjects.Count == 0) { - host.GetClipboard()?.SetText($"{clock.CurrentTime.ToEditorFormattedString()} - "); - return; + builder.Append(clock.CurrentTime.ToEditorFormattedString()); + } + else + { + var orderedHitObjects = editorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime); + builder.Append(orderedHitObjects.FirstOrDefault().StartTime.ToEditorFormattedString()); + builder.Append($" ({string.Join(',', orderedHitObjects.Cast().Select(h => h.IndexInCurrentCombo + 1))})"); + + clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); } - host.GetClipboard()?.SetText($"{editorBeatmap.SelectedHitObjects.FirstOrDefault().StartTime.ToEditorFormattedString()} ({string.Join(',', editorBeatmap.SelectedHitObjects.Select(h => ((h as IHasComboInformation)?.IndexInCurrentCombo + 1 ?? 0).ToString()))}) - "); - clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); + builder.Append(suffix); + host.GetClipboard()?.SetText(builder.ToString()); } protected void Paste() From 51e0304c54a604bab6d6c8007c59ec755b115b2d Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Wed, 17 Mar 2021 18:31:09 +0800 Subject: [PATCH 17/94] properly format strings per ruleset --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 ++ osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs | 2 ++ osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 ++ osu.Game/Rulesets/Objects/HitObject.cs | 2 ++ osu.Game/Screens/Edit/Editor.cs | 2 +- 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index ae45182960..631b50d686 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -120,5 +120,7 @@ namespace osu.Game.Rulesets.Catch.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + public override string ToEditorString() => (IndexInCurrentCombo + 1).ToString(); } } diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index 27bf50493d..c43d223335 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Mania.Objects protected override HitWindows CreateHitWindows() => new ManiaHitWindows(); + public override string ToEditorString() => $"{StartTime}|{Column}"; + #region LegacyBeatmapEncoder float IHasXPosition.X => Column; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 22b64af3df..e784d13084 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -130,5 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects } protected override HitWindows CreateHitWindows() => new OsuHitWindows(); + + public override string ToEditorString() => (IndexInCurrentCombo + 1).ToString(); } } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 826d411822..fa7b2811cc 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -168,6 +168,8 @@ namespace osu.Game.Rulesets.Objects /// [NotNull] protected virtual HitWindows CreateHitWindows() => new HitWindows(); + + public virtual string ToEditorString() => string.Empty; } public static class HitObjectExtensions diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index fdb31a8b8c..a6e84d59a7 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -558,7 +558,7 @@ namespace osu.Game.Screens.Edit { var orderedHitObjects = editorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime); builder.Append(orderedHitObjects.FirstOrDefault().StartTime.ToEditorFormattedString()); - builder.Append($" ({string.Join(',', orderedHitObjects.Cast().Select(h => h.IndexInCurrentCombo + 1))})"); + builder.Append($" ({string.Join(',', orderedHitObjects.Select(h => h.ToEditorString()))})"); clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); } From 563a0584d589bf0dce7c29fffc03268b097db485 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 18:48:51 +0900 Subject: [PATCH 18/94] Implement editor timeline stacking support --- .../Timeline/TimelineBlueprintContainer.cs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 1fc529910b..4522418e87 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -2,6 +2,8 @@ // 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.Graphics; @@ -121,6 +123,46 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } base.Update(); + + updateStacking(); + } + + private void updateStacking() + { + // because only blueprints of objects which are alive (via pooling) are displayed in the timeline, it's feasible to do this every-update. + + const int stack_offset = 5; + + // after the stack gets this tall, we can presume there is space underneath to draw subsequent blueprints. + const int stack_reset_count = 3; + + Stack currentConcurrentObjects = new Stack(); + + foreach (var b in SelectionBlueprints.Reverse()) + { + while (currentConcurrentObjects.TryPeek(out double stackEndTime)) + { + if (Precision.AlmostBigger(stackEndTime, b.HitObject.StartTime, 1)) + break; + + currentConcurrentObjects.Pop(); + } + + b.Y = -(stack_offset * currentConcurrentObjects.Count); + + var bEndTime = b.HitObject.GetEndTime(); + + // if the stack gets too high, we should have space below it to display the next batch of objects. + // importantly, we only do this if time has incremented, else a stack of hitobjects all at the same time value would start to overlap themselves. + if (!currentConcurrentObjects.TryPeek(out double nextStackEndTime) || + !Precision.AlmostEquals(nextStackEndTime, bEndTime, 1)) + { + if (currentConcurrentObjects.Count >= stack_reset_count) + currentConcurrentObjects.Clear(); + } + + currentConcurrentObjects.Push(bEndTime); + } } protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler(); @@ -203,7 +245,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Box.X = Math.Min(rescaledStart, rescaledEnd); Box.Width = Math.Abs(rescaledStart - rescaledEnd); - PerformSelection?.Invoke(Box.ScreenSpaceDrawQuad.AABBFloat); + var boxScreenRect = Box.ScreenSpaceDrawQuad.AABBFloat; + + // we don't care about where the hitobjects are vertically. in cases like stacking display, they may be outside the box without this adjustment. + boxScreenRect.Y -= boxScreenRect.Height; + boxScreenRect.Height *= 2; + + PerformSelection?.Invoke(boxScreenRect); } public override void Hide() From 71a361337da8ee2ddac2feeb8af3e9644248c3d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 Mar 2021 21:57:48 +0900 Subject: [PATCH 19/94] Add comment regarding usage of `Reverse()` Co-authored-by: Dan Balasescu --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 4522418e87..3526f264a7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -138,6 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Stack currentConcurrentObjects = new Stack(); + // Reversing is done to enumerate in order of increasing StartTime. foreach (var b in SelectionBlueprints.Reverse()) { while (currentConcurrentObjects.TryPeek(out double stackEndTime)) From def0e5c42e90e5e8eb984c794a31eee0567caee4 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Tue, 23 Mar 2021 17:21:42 +0100 Subject: [PATCH 20/94] 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 21/94] 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 22/94] 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 7b3336783fb3e4864680fa69d2ea8240d3ac93b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 15:24:59 +0900 Subject: [PATCH 23/94] Stabilise ordering instead of simple reversing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartล‚omiej Dach --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 3526f264a7..09da613a04 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -138,8 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Stack currentConcurrentObjects = new Stack(); - // Reversing is done to enumerate in order of increasing StartTime. - foreach (var b in SelectionBlueprints.Reverse()) + foreach (var b in SelectionBlueprints.OrderBy(b => b.HitObject.StartTime).ThenBy(b => b.HitObject.GetEndTime())) { while (currentConcurrentObjects.TryPeek(out double stackEndTime)) { From 9fdd23b134744140c66a5e8595fb1d655c038f83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 16:28:30 +0900 Subject: [PATCH 24/94] Fix various issues with stacking --- .../Timeline/TimelineBlueprintContainer.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 09da613a04..cec851b5bb 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -136,41 +136,42 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // after the stack gets this tall, we can presume there is space underneath to draw subsequent blueprints. const int stack_reset_count = 3; - Stack currentConcurrentObjects = new Stack(); + Stack currentConcurrentObjects = new Stack(); foreach (var b in SelectionBlueprints.OrderBy(b => b.HitObject.StartTime).ThenBy(b => b.HitObject.GetEndTime())) { - while (currentConcurrentObjects.TryPeek(out double stackEndTime)) + // remove objects from the stack as long as their end time is in the past. + while (currentConcurrentObjects.TryPeek(out HitObject hitObject)) { - if (Precision.AlmostBigger(stackEndTime, b.HitObject.StartTime, 1)) + if (Precision.AlmostBigger(hitObject.GetEndTime(), b.HitObject.StartTime, 1)) break; currentConcurrentObjects.Pop(); } - b.Y = -(stack_offset * currentConcurrentObjects.Count); - - var bEndTime = b.HitObject.GetEndTime(); - // if the stack gets too high, we should have space below it to display the next batch of objects. // importantly, we only do this if time has incremented, else a stack of hitobjects all at the same time value would start to overlap themselves. - if (!currentConcurrentObjects.TryPeek(out double nextStackEndTime) || - !Precision.AlmostEquals(nextStackEndTime, bEndTime, 1)) + if (currentConcurrentObjects.TryPeek(out HitObject h) && !Precision.AlmostEquals(h.StartTime, b.HitObject.StartTime, 1)) { if (currentConcurrentObjects.Count >= stack_reset_count) currentConcurrentObjects.Clear(); } - currentConcurrentObjects.Push(bEndTime); + b.Y = -(stack_offset * currentConcurrentObjects.Count); + + currentConcurrentObjects.Push(b.HitObject); } } protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler(); - protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject) + protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) { - OnDragHandled = handleScrollViaDrag - }; + return new TimelineHitObjectBlueprint(hitObject) + { + OnDragHandled = handleScrollViaDrag + }; + } protected override DragBox CreateDragBox(Action performSelect) => new TimelineDragBox(performSelect); From 013ddc734c09c110b9590b71cd1f04aa1a5bf021 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:04:35 +0900 Subject: [PATCH 25/94] 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 26/94] 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 cb4ae6e61a87745a3204baa7459c5f895dc5b9e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:34:29 +0900 Subject: [PATCH 27/94] Fix very high accuracies feigning as "perfect" 100% plays when actually below --- osu.Game/Users/UserStatistics.cs | 2 +- osu.Game/Utils/FormatUtils.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index dc926898fc..04a358436e 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -42,7 +42,7 @@ namespace osu.Game.Users public long RankedScore; [JsonProperty(@"hit_accuracy")] - public decimal Accuracy; + public double Accuracy; [JsonIgnore] public string DisplayAccuracy => Accuracy.FormatAccuracy(); diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index 2578d8d835..70a96367be 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.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 Humanizer; namespace osu.Game.Utils @@ -12,14 +13,14 @@ namespace osu.Game.Utils /// /// The accuracy to be formatted /// formatted accuracy in percentage - public static string FormatAccuracy(this double accuracy) => $"{accuracy:0.00%}"; + public static string FormatAccuracy(this double accuracy) + { + // we don't ever want to show 100% when the accuracy is below perfect, even if it rounds to 100%. + if (accuracy < 1 && accuracy > 0.9999) + accuracy = 0.9999; - /// - /// Turns the provided accuracy into a percentage with 2 decimal places. - /// - /// The accuracy to be formatted - /// formatted accuracy in percentage - public static string FormatAccuracy(this decimal accuracy) => $"{accuracy:0.00}%"; + return $"{accuracy:0.00%}"; + } /// /// Formats the supplied rank/leaderboard position in a consistent, simplified way. From 544117a49495a97c297f0253b0fcfa1725b9ce74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:34:41 +0900 Subject: [PATCH 28/94] Add test coverage of accuracy formatting function --- osu.Game.Tests/NonVisual/FormatUtilsTest.cs | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/FormatUtilsTest.cs diff --git a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs new file mode 100644 index 0000000000..eb738f5f67 --- /dev/null +++ b/osu.Game.Tests/NonVisual/FormatUtilsTest.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 NUnit.Framework; +using osu.Game.Utils; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class FormatUtilsTest + { + [TestCase(0, "0.00%")] + [TestCase(0.01, "1.00%")] + [TestCase(0.9899, "98.99%")] + [TestCase(0.989999, "99.00%")] + [TestCase(0.99, "99.00%")] + [TestCase(0.9999, "99.99%")] + [TestCase(0.999999, "99.99%")] + [TestCase(1, "100.00%")] + public void TestAccuracyFormatting(double input, string expectedOutput) + { + Assert.AreEqual(expectedOutput, input.FormatAccuracy()); + } + } +} From 701342e03673b4e9d647484a4193418fbf64d107 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:43:51 +0900 Subject: [PATCH 29/94] Remove accuracy rounding at a ScoreProcessor level --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 2024290460..b81fa79345 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -337,7 +337,7 @@ namespace osu.Game.Rulesets.Scoring score.TotalScore = (long)Math.Round(GetStandardisedScore()); score.Combo = Combo.Value; score.MaxCombo = HighestCombo.Value; - score.Accuracy = Math.Round(Accuracy.Value, 4); + score.Accuracy = Accuracy.Value; score.Rank = Rank.Value; score.Date = DateTimeOffset.Now; From 630faa3b56e5a3b36577c695f906d006088e61e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:50:21 +0900 Subject: [PATCH 30/94] Add TODO marking incorrect EF core data type As mentioned in the comment, we probably don't need to be storing this in the database in the first place (as it should be able to be calculated from the other statistics we have available to us). Something to consider when we refactor the database backend. --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index f5192f3a40..ef11c19e3f 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -30,7 +30,7 @@ namespace osu.Game.Scoring public long TotalScore { get; set; } [JsonProperty("accuracy")] - [Column(TypeName = "DECIMAL(1,4)")] + [Column(TypeName = "DECIMAL(1,4)")] // TODO: This data type is wrong (should contain more precision). But at the same time, we probably don't need to be storing this in the database. public double Accuracy { get; set; } [JsonIgnore] From 77888ae6402c669a0f6330d698b6794bc9ff5539 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 17:51:20 +0900 Subject: [PATCH 31/94] Remove unnecessary using --- osu.Game/Utils/FormatUtils.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index 70a96367be..4c7702f04e 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.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 Humanizer; namespace osu.Game.Utils From f6647de7693de11ad68b8316eb17483bd9bb3126 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Mar 2021 19:56:26 +0900 Subject: [PATCH 32/94] 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 bada1e7189973f09452ad51419abbb79650f9972 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 25 Mar 2021 15:00:06 +0300 Subject: [PATCH 33/94] Update legacy spinner bonus counter usage --- .../Skinning/Legacy/LegacySpinner.cs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index dd1c6cad77..064b7a4680 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -48,9 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy DrawableSpinner = (DrawableSpinner)drawableHitObject; - Container overlayContainer; - - AddInternal(overlayContainer = new Container + AddInternal(new Container { Depth = float.MinValue, RelativeSizeAxes = Axes.Both, @@ -73,21 +71,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Scale = new Vector2(SPRITE_SCALE), Y = SPINNER_TOP_OFFSET + 115, }, + bonusCounter = new LegacySpriteText(source, LegacyFont.Score) + { + Alpha = 0f, + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Scale = new Vector2(SPRITE_SCALE), + Y = SPINNER_TOP_OFFSET + 299, + }.With(s => s.Font = s.Font.With(fixedWidth: false)), } }); - - bonusCounter = (source.GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreText)) as LegacySpriteText)?.With(c => - { - c.Alpha = 0f; - c.Anchor = Anchor.TopCentre; - c.Origin = Anchor.Centre; - c.Font = c.Font.With(fixedWidth: false); - c.Scale = new Vector2(SPRITE_SCALE); - c.Y = SPINNER_TOP_OFFSET + 299; - }); - - if (bonusCounter != null) - overlayContainer.Add(bonusCounter); } private IBindable gainedBonus; @@ -98,16 +91,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.LoadComplete(); - if (bonusCounter != null) + gainedBonus = DrawableSpinner.GainedBonus.GetBoundCopy(); + gainedBonus.BindValueChanged(bonus => { - gainedBonus = DrawableSpinner.GainedBonus.GetBoundCopy(); - gainedBonus.BindValueChanged(bonus => - { - bonusCounter.Text = bonus.NewValue.ToString(NumberFormatInfo.InvariantInfo); - bonusCounter.FadeOutFromOne(800, Easing.Out); - bonusCounter.ScaleTo(SPRITE_SCALE * 2f).Then().ScaleTo(SPRITE_SCALE * 1.28f, 800, Easing.Out); - }); - } + bonusCounter.Text = bonus.NewValue.ToString(NumberFormatInfo.InvariantInfo); + bonusCounter.FadeOutFromOne(800, Easing.Out); + bonusCounter.ScaleTo(SPRITE_SCALE * 2f).Then().ScaleTo(SPRITE_SCALE * 1.28f, 800, Easing.Out); + }); completed.BindValueChanged(onCompletedChanged, true); From 6a7f9261684602ab3b4633839a1bea1e0acd035c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 13:06:30 +0900 Subject: [PATCH 34/94] Change rounding to use a more general flooring approach --- osu.Game.Tests/NonVisual/FormatUtilsTest.cs | 2 +- osu.Game/Utils/FormatUtils.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs index eb738f5f67..46d8f4fec4 100644 --- a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs +++ b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests.NonVisual [TestCase(0, "0.00%")] [TestCase(0.01, "1.00%")] [TestCase(0.9899, "98.99%")] - [TestCase(0.989999, "99.00%")] + [TestCase(0.989999, "98.99%")] [TestCase(0.99, "99.00%")] [TestCase(0.9999, "99.99%")] [TestCase(0.999999, "99.99%")] diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index 4c7702f04e..8312b9e756 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.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 Humanizer; namespace osu.Game.Utils @@ -14,9 +15,11 @@ namespace osu.Game.Utils /// formatted accuracy in percentage public static string FormatAccuracy(this double accuracy) { - // we don't ever want to show 100% when the accuracy is below perfect, even if it rounds to 100%. - if (accuracy < 1 && accuracy > 0.9999) - accuracy = 0.9999; + // for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number. + // ie. a score which gets 89.99999% shouldn't ever show as 90%. + // the reasoning for this is that cutoffs for grade increases are at whole numbers and displaying the required + // percentile with a non-matching grade is confusing. + accuracy = Math.Floor(accuracy * 10000) / 10000; return $"{accuracy:0.00%}"; } From 4909eaf890715b10a6728270673a7c629f4a7493 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 13:09:53 +0900 Subject: [PATCH 35/94] Add the ability to specify format provider (to make tests culture invariant) --- osu.Game.Tests/NonVisual/FormatUtilsTest.cs | 3 ++- osu.Game/Utils/FormatUtils.cs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs b/osu.Game.Tests/NonVisual/FormatUtilsTest.cs index 46d8f4fec4..df095ddee3 100644 --- a/osu.Game.Tests/NonVisual/FormatUtilsTest.cs +++ b/osu.Game.Tests/NonVisual/FormatUtilsTest.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.Globalization; using NUnit.Framework; using osu.Game.Utils; @@ -19,7 +20,7 @@ namespace osu.Game.Tests.NonVisual [TestCase(1, "100.00%")] public void TestAccuracyFormatting(double input, string expectedOutput) { - Assert.AreEqual(expectedOutput, input.FormatAccuracy()); + Assert.AreEqual(expectedOutput, input.FormatAccuracy(CultureInfo.InvariantCulture)); } } } diff --git a/osu.Game/Utils/FormatUtils.cs b/osu.Game/Utils/FormatUtils.cs index 8312b9e756..df1b6cf00d 100644 --- a/osu.Game/Utils/FormatUtils.cs +++ b/osu.Game/Utils/FormatUtils.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Globalization; using Humanizer; namespace osu.Game.Utils @@ -11,9 +12,10 @@ namespace osu.Game.Utils /// /// Turns the provided accuracy into a percentage with 2 decimal places. /// - /// The accuracy to be formatted + /// The accuracy to be formatted. + /// An optional format provider. /// formatted accuracy in percentage - public static string FormatAccuracy(this double accuracy) + public static string FormatAccuracy(this double accuracy, IFormatProvider formatProvider = null) { // for the sake of display purposes, we don't want to show a user a "rounded up" percentage to the next whole number. // ie. a score which gets 89.99999% shouldn't ever show as 90%. @@ -21,7 +23,7 @@ namespace osu.Game.Utils // percentile with a non-matching grade is confusing. accuracy = Math.Floor(accuracy * 10000) / 10000; - return $"{accuracy:0.00%}"; + return accuracy.ToString("0.00%", formatProvider ?? CultureInfo.CurrentCulture); } /// From 17a0b19ee71c09f52baafabdadec7bd71aaafee8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 14:04:09 +0900 Subject: [PATCH 36/94] Always set progress before updating download state Without doing so, a state change may read from an old progress value. --- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 52042c266b..d9599481e7 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -123,13 +123,13 @@ namespace osu.Game.Online { if (attachedRequest.Progress == 1) { - State.Value = DownloadState.Importing; Progress.Value = 1; + State.Value = DownloadState.Importing; } else { - State.Value = DownloadState.Downloading; Progress.Value = attachedRequest.Progress; + State.Value = DownloadState.Downloading; attachedRequest.Failure += onRequestFailure; attachedRequest.DownloadProgressed += onRequestProgress; From 0a889fafc4fe0df932aeca07939cf4a6cc34c1f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 14:04:39 +0900 Subject: [PATCH 37/94] Don't send progress updates when not in a downloading state This is mostly just a sanity/debounce check. --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs index d6f4c45a75..8278162353 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs @@ -45,6 +45,9 @@ namespace osu.Game.Online.Rooms Progress.BindValueChanged(_ => { + if (State.Value != DownloadState.Downloading) + return; + // incoming progress changes are going to be at a very high rate. // we don't want to flood the network with this, so rate limit how often we send progress updates. if (progressUpdate?.Completed != false) From 7faca766e45b0d369326a9bdc26422bb065c825c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 14:49:02 +0900 Subject: [PATCH 38/94] Fix crash on attempting to join lobby using key press during a pending join Closes https://github.com/ppy/osu/issues/12040. --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f13d623eae..5a28d0f968 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -170,7 +170,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - Debug.Assert(joiningRoomOperation == null); + if (joiningRoomOperation != null) + return; + joiningRoomOperation = ongoingOperationTracker?.BeginOperation(); RoomManager?.JoinRoom(room, r => From 6a4157d1939f513d0ed1167192b9b7007e8db1d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 16:13:43 +0900 Subject: [PATCH 39/94] Remove unused using statement --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 5a28d0f968..f24577a8a5 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; From 2bea69456e8bfbcdf780bd6e8adab5002913f3e3 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 26 Mar 2021 15:24:33 +0800 Subject: [PATCH 40/94] remove implementations --- .../Objects/CatchHitObject.cs | 2 -- .../Objects/ManiaHitObject.cs | 2 -- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 -- osu.Game/Rulesets/Objects/HitObject.cs | 2 -- osu.Game/Screens/Edit/Editor.cs | 23 +++---------------- 5 files changed, 3 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 631b50d686..ae45182960 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -120,7 +120,5 @@ namespace osu.Game.Rulesets.Catch.Objects } protected override HitWindows CreateHitWindows() => HitWindows.Empty; - - public override string ToEditorString() => (IndexInCurrentCombo + 1).ToString(); } } diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs index c43d223335..27bf50493d 100644 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Mania.Objects protected override HitWindows CreateHitWindows() => new ManiaHitWindows(); - public override string ToEditorString() => $"{StartTime}|{Column}"; - #region LegacyBeatmapEncoder float IHasXPosition.X => Column; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index e784d13084..22b64af3df 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -130,7 +130,5 @@ namespace osu.Game.Rulesets.Osu.Objects } protected override HitWindows CreateHitWindows() => new OsuHitWindows(); - - public override string ToEditorString() => (IndexInCurrentCombo + 1).ToString(); } } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index fa7b2811cc..826d411822 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -168,8 +168,6 @@ namespace osu.Game.Rulesets.Objects /// [NotNull] protected virtual HitWindows CreateHitWindows() => new HitWindows(); - - public virtual string ToEditorString() => string.Empty; } public static class HitObjectExtensions diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a6e84d59a7..c2a9fd49b1 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -63,9 +63,6 @@ namespace osu.Game.Screens.Edit protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash; - [Resolved] - private GameHost host { get; set; } - [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -110,7 +107,7 @@ namespace osu.Game.Screens.Edit private MusicController music { get; set; } [BackgroundDependencyLoader] - private void load(OsuColour colours, OsuConfigManager config) + private void load(OsuColour colours, GameHost host, OsuConfigManager config) { if (Beatmap.Value is DummyWorkingBeatmap) { @@ -547,24 +544,10 @@ namespace osu.Game.Screens.Edit protected void Copy() { - var builder = new StringBuilder(); - const string suffix = " - "; - if (editorBeatmap.SelectedHitObjects.Count == 0) - { - builder.Append(clock.CurrentTime.ToEditorFormattedString()); - } - else - { - var orderedHitObjects = editorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime); - builder.Append(orderedHitObjects.FirstOrDefault().StartTime.ToEditorFormattedString()); - builder.Append($" ({string.Join(',', orderedHitObjects.Select(h => h.ToEditorString()))})"); + return; - clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); - } - - builder.Append(suffix); - host.GetClipboard()?.SetText(builder.ToString()); + clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); } protected void Paste() From b8b7eb4c4b8e0af156ebdad46a83ecbe3427b0b2 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 26 Mar 2021 15:25:20 +0800 Subject: [PATCH 41/94] refactor logic to its own component and handle hit object to string conversion to its ruleset-specific composers --- .../Edit/ManiaHitObjectComposer.cs | 3 ++ .../Edit/OsuHitObjectComposer.cs | 3 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 + .../Screens/Edit/Compose/ComposeScreen.cs | 23 +++++++++- osu.Game/Screens/Edit/SelectionHelper.cs | 46 +++++++++++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Edit/SelectionHelper.cs diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 324670c4b2..4cb34a217c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -119,5 +119,8 @@ namespace osu.Game.Rulesets.Mania.Edit beatSnapGrid.SelectionTimeRange = null; } } + + public override IEnumerable ConvertSelectionToString() + => EditorBeatmap.SelectedHitObjects.Cast().Select(h => $"{h.StartTime}|{h.Column}"); } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0490e8b8ce..1943f52c73 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -82,6 +82,9 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(this); + public override IEnumerable ConvertSelectionToString() + => selectedHitObjects.Cast().Select(h => (h.IndexInCurrentCombo + 1).ToString()); + private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index e927951d0a..eee16043e4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -438,6 +438,8 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } + public virtual IEnumerable ConvertSelectionToString() => Array.Empty(); + #region IPositionSnapProvider public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 81b1195a40..c63ef11c74 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -6,6 +6,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; @@ -14,16 +16,19 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Edit.Compose { - public class ComposeScreen : EditorScreenWithTimeline + public class ComposeScreen : EditorScreenWithTimeline, IKeyBindingHandler { [Resolved] private IBindable beatmap { get; set; } private HitObjectComposer composer; + private SelectionHelper helper; + public ComposeScreen() : base(EditorScreenMode.Compose) { + Add(helper = new SelectionHelper()); } private Ruleset ruleset; @@ -72,5 +77,21 @@ namespace osu.Game.Screens.Edit.Compose // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(content)); } + + public bool OnPressed(PlatformAction action) + { + switch (action.ActionType) + { + case PlatformActionType.Copy: + helper.CopySelectionToClipboard(); + return false; + default: + return false; + }; + } + + public void OnReleased(PlatformAction action) + { + } } } diff --git a/osu.Game/Screens/Edit/SelectionHelper.cs b/osu.Game/Screens/Edit/SelectionHelper.cs new file mode 100644 index 0000000000..e0eb868c9f --- /dev/null +++ b/osu.Game/Screens/Edit/SelectionHelper.cs @@ -0,0 +1,46 @@ +๏ปฟusing System.Linq; +using System.Text; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Game.Extensions; +using osu.Game.Rulesets.Edit; + +namespace osu.Game.Screens.Edit +{ + public class SelectionHelper : Component + { + [Resolved] + private GameHost host { get; set; } + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + [Resolved(CanBeNull = true)] + private HitObjectComposer composer { get; set; } + + public void CopySelectionToClipboard() + { + host.GetClipboard().SetText(formatSelectionAsString()); + } + + private string formatSelectionAsString() + { + const string separator = " - "; + var builder = new StringBuilder(); + + if (!editorBeatmap.SelectedHitObjects.Any()) + { + builder.Append($"{clock.CurrentTime.ToEditorFormattedString()}{separator}"); + return builder.ToString(); + }; + + builder.Append(editorBeatmap.SelectedHitObjects.First().StartTime.ToEditorFormattedString()); + builder.Append($" ({string.Join(',', composer.ConvertSelectionToString())}){separator}"); + return builder.ToString(); + } + } +} From cb48e5f15822a7aab53317bc8a1b938c777f8544 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 Mar 2021 16:33:16 +0900 Subject: [PATCH 42/94] Fix timeline not visually ordering hitobjects in a stable way --- .../Components/HitObjectOrderedSelectionContainer.cs | 8 +++++++- .../Components/Timeline/TimelineBlueprintContainer.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index 9e95fe4fa1..d612cf3fe0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -71,7 +71,13 @@ namespace osu.Game.Screens.Edit.Compose.Components // Put earlier blueprints towards the end of the list, so they handle input first int i = yObj.HitObject.StartTime.CompareTo(xObj.HitObject.StartTime); - return i == 0 ? CompareReverseChildID(x, y) : i; + + if (i != 0) return i; + + // Fall back to end time if the start time is equal. + i = yObj.HitObject.GetEndTime().CompareTo(xObj.HitObject.GetEndTime()); + + return i == 0 ? CompareReverseChildID(y, x) : i; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index cec851b5bb..3623f8ad8e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -138,7 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Stack currentConcurrentObjects = new Stack(); - foreach (var b in SelectionBlueprints.OrderBy(b => b.HitObject.StartTime).ThenBy(b => b.HitObject.GetEndTime())) + foreach (var b in SelectionBlueprints.Reverse()) { // remove objects from the stack as long as their end time is in the past. while (currentConcurrentObjects.TryPeek(out HitObject hitObject)) From 374f8c5e229b314a2f7cf1de9b29285cccbff0d2 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 26 Mar 2021 15:33:28 +0800 Subject: [PATCH 43/94] move to compose namespace and add license header --- osu.Game/Screens/Edit/{ => Compose}/SelectionHelper.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename osu.Game/Screens/Edit/{ => Compose}/SelectionHelper.cs (85%) diff --git a/osu.Game/Screens/Edit/SelectionHelper.cs b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs similarity index 85% rename from osu.Game/Screens/Edit/SelectionHelper.cs rename to osu.Game/Screens/Edit/Compose/SelectionHelper.cs index e0eb868c9f..39d6d57a2a 100644 --- a/osu.Game/Screens/Edit/SelectionHelper.cs +++ b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs @@ -1,4 +1,7 @@ -๏ปฟusing System.Linq; +๏ปฟ// 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 System.Text; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -6,7 +9,7 @@ using osu.Framework.Platform; using osu.Game.Extensions; using osu.Game.Rulesets.Edit; -namespace osu.Game.Screens.Edit +namespace osu.Game.Screens.Edit.Compose { public class SelectionHelper : Component { From 71a0616861916129456e0a301d0386269c51cd6c Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 26 Mar 2021 15:34:45 +0800 Subject: [PATCH 44/94] remove extra semi colons --- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 2 +- osu.Game/Screens/Edit/Compose/SelectionHelper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index c63ef11c74..b249ae9bcd 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit.Compose return false; default: return false; - }; + } } public void OnReleased(PlatformAction action) diff --git a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs index 39d6d57a2a..2e172c12dc 100644 --- a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs +++ b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Compose { builder.Append($"{clock.CurrentTime.ToEditorFormattedString()}{separator}"); return builder.ToString(); - }; + } builder.Append(editorBeatmap.SelectedHitObjects.First().StartTime.ToEditorFormattedString()); builder.Append($" ({string.Join(',', composer.ConvertSelectionToString())}){separator}"); From 1b6e08b3eb34f80afc5126c2af1efb7ba16d8d19 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 26 Mar 2021 10:35:46 +0300 Subject: [PATCH 45/94] Fix incorrect default font overlap for score and combo --- osu.Game/Skinning/LegacySkinExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index d08f50bccb..d8fb1fa664 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -97,10 +97,10 @@ namespace osu.Game.Skinning switch (font) { case LegacyFont.Score: - return source.GetConfig(LegacySetting.ScoreOverlap)?.Value ?? -2f; + return source.GetConfig(LegacySetting.ScoreOverlap)?.Value ?? 0f; case LegacyFont.Combo: - return source.GetConfig(LegacySetting.ComboOverlap)?.Value ?? -2f; + return source.GetConfig(LegacySetting.ComboOverlap)?.Value ?? 0f; case LegacyFont.HitCircle: return source.GetConfig(LegacySetting.HitCircleOverlap)?.Value ?? -2f; From c96321206a0e5157d8864e5d84204706f918ce30 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Fri, 26 Mar 2021 16:17:24 +0800 Subject: [PATCH 46/94] fix appveyor complaints --- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 3 ++- osu.Game/Screens/Edit/Editor.cs | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index b249ae9bcd..16043ff64b 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Compose private HitObjectComposer composer; - private SelectionHelper helper; + private readonly SelectionHelper helper; public ComposeScreen() : base(EditorScreenMode.Compose) @@ -85,6 +85,7 @@ namespace osu.Game.Screens.Edit.Compose case PlatformActionType.Copy: helper.CopySelectionToClipboard(); return false; + default: return false; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c2a9fd49b1..d9ba12d331 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -17,12 +16,10 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Logging; -using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; @@ -31,7 +28,6 @@ using osu.Game.IO.Serialization; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; @@ -107,7 +103,7 @@ namespace osu.Game.Screens.Edit private MusicController music { get; set; } [BackgroundDependencyLoader] - private void load(OsuColour colours, GameHost host, OsuConfigManager config) + private void load(OsuColour colours, OsuConfigManager config) { if (Beatmap.Value is DummyWorkingBeatmap) { From 5d272bef9778d590b84e1e97edfde3d64a8b68de Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Fri, 26 Mar 2021 16:28:04 +0100 Subject: [PATCH 47/94] 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 48/94] 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 49/94] 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 50/94] 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 21398e25b5b939ae40e15be42bb6ffb93365d315 Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Sat, 27 Mar 2021 10:02:21 +0800 Subject: [PATCH 51/94] null check composer and ensure the correct start time from selected hit objects --- osu.Game/Screens/Edit/Compose/SelectionHelper.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs index 2e172c12dc..a01a9e0144 100644 --- a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs +++ b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs @@ -41,8 +41,10 @@ namespace osu.Game.Screens.Edit.Compose return builder.ToString(); } - builder.Append(editorBeatmap.SelectedHitObjects.First().StartTime.ToEditorFormattedString()); - builder.Append($" ({string.Join(',', composer.ConvertSelectionToString())}){separator}"); + string hitObjects = composer != null ? string.Join(',', composer.ConvertSelectionToString()) : string.Empty; + + builder.Append(editorBeatmap.SelectedHitObjects.Min(h => h.StartTime).ToEditorFormattedString()); + builder.Append($" ({hitObjects}){separator}"); return builder.ToString(); } } From 699a317b445ff557163d91143682ab6f6c5faf8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 18:07:47 +0900 Subject: [PATCH 52/94] Fix chat scroll sticking visually when scrolling beyond bottom extent --- osu.Game/Overlays/Chat/DrawableChannel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 86ce724390..5f9c00b36a 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -275,7 +275,8 @@ namespace osu.Game.Overlays.Chat { if (!UserScrolling) { - ScrollToEnd(); + if (Current < ScrollableExtent) + ScrollToEnd(); lastExtent = ScrollableExtent; } }); From 9a02f3868c2105e51dbd7e64ce4443d08938d02e Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Mon, 29 Mar 2021 17:29:05 +0800 Subject: [PATCH 53/94] return a string instead --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 4 ++-- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 4 ++-- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 4cb34a217c..d9570bf8be 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Mania.Edit } } - public override IEnumerable ConvertSelectionToString() - => EditorBeatmap.SelectedHitObjects.Cast().Select(h => $"{h.StartTime}|{h.Column}"); + public override string ConvertSelectionToString() + => string.Join(',', EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 1943f52c73..396fd41377 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -82,8 +82,8 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(this); - public override IEnumerable ConvertSelectionToString() - => selectedHitObjects.Cast().Select(h => (h.IndexInCurrentCombo + 1).ToString()); + public override string ConvertSelectionToString() + => string.Join(',', selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index eee16043e4..736fc47dee 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -438,7 +438,7 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } - public virtual IEnumerable ConvertSelectionToString() => Array.Empty(); + public virtual string ConvertSelectionToString() => string.Empty; #region IPositionSnapProvider From cdb779f764bb09dcba1784f66fc87aab9debfd8b Mon Sep 17 00:00:00 2001 From: Nathan Alo Date: Mon, 29 Mar 2021 17:30:23 +0800 Subject: [PATCH 54/94] move copy logic inside ComposeScreen --- .../Screens/Edit/Compose/ComposeScreen.cs | 37 ++++++++++---- .../Screens/Edit/Compose/SelectionHelper.cs | 51 ------------------- 2 files changed, 26 insertions(+), 62 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Compose/SelectionHelper.cs diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 16043ff64b..f6ce5a4e3d 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -2,13 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using System.Linq; +using System.Text; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; @@ -21,14 +25,17 @@ namespace osu.Game.Screens.Edit.Compose [Resolved] private IBindable beatmap { get; set; } - private HitObjectComposer composer; + [Resolved] + private GameHost host { get; set; } - private readonly SelectionHelper helper; + [Resolved] + private EditorClock clock { get; set; } + + private HitObjectComposer composer; public ComposeScreen() : base(EditorScreenMode.Compose) { - Add(helper = new SelectionHelper()); } private Ruleset ruleset; @@ -78,17 +85,25 @@ namespace osu.Game.Screens.Edit.Compose return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(content)); } + private string formatSelectionAsString() + { + var builder = new StringBuilder(); + builder.Append(EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).FirstOrDefault()?.StartTime.ToEditorFormattedString() ?? clock.CurrentTime.ToEditorFormattedString()); + + if (EditorBeatmap.SelectedHitObjects.Any() && composer != null) + builder.Append($" ({composer.ConvertSelectionToString()})"); + + builder.Append(" - "); + + return builder.ToString(); + } + public bool OnPressed(PlatformAction action) { - switch (action.ActionType) - { - case PlatformActionType.Copy: - helper.CopySelectionToClipboard(); - return false; + if (action.ActionType == PlatformActionType.Copy) + host.GetClipboard().SetText(formatSelectionAsString()); - default: - return false; - } + return false; } public void OnReleased(PlatformAction action) diff --git a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs b/osu.Game/Screens/Edit/Compose/SelectionHelper.cs deleted file mode 100644 index a01a9e0144..0000000000 --- a/osu.Game/Screens/Edit/Compose/SelectionHelper.cs +++ /dev/null @@ -1,51 +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.Linq; -using System.Text; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Platform; -using osu.Game.Extensions; -using osu.Game.Rulesets.Edit; - -namespace osu.Game.Screens.Edit.Compose -{ - public class SelectionHelper : Component - { - [Resolved] - private GameHost host { get; set; } - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - - [Resolved(CanBeNull = true)] - private HitObjectComposer composer { get; set; } - - public void CopySelectionToClipboard() - { - host.GetClipboard().SetText(formatSelectionAsString()); - } - - private string formatSelectionAsString() - { - const string separator = " - "; - var builder = new StringBuilder(); - - if (!editorBeatmap.SelectedHitObjects.Any()) - { - builder.Append($"{clock.CurrentTime.ToEditorFormattedString()}{separator}"); - return builder.ToString(); - } - - string hitObjects = composer != null ? string.Join(',', composer.ConvertSelectionToString()) : string.Empty; - - builder.Append(editorBeatmap.SelectedHitObjects.Min(h => h.StartTime).ToEditorFormattedString()); - builder.Append($" ({hitObjects}){separator}"); - return builder.ToString(); - } - } -} From 90ab765cf519be8be956175f492f3c7aff4e0f0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 18:46:32 +0900 Subject: [PATCH 55/94] Reorder methods and surround with region --- .../Screens/Edit/Compose/ComposeScreen.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index f6ce5a4e3d..1d52cc60fd 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -85,6 +85,20 @@ namespace osu.Game.Screens.Edit.Compose return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(content)); } + #region Input Handling + + public bool OnPressed(PlatformAction action) + { + if (action.ActionType == PlatformActionType.Copy) + host.GetClipboard().SetText(formatSelectionAsString()); + + return false; + } + + public void OnReleased(PlatformAction action) + { + } + private string formatSelectionAsString() { var builder = new StringBuilder(); @@ -98,16 +112,6 @@ namespace osu.Game.Screens.Edit.Compose return builder.ToString(); } - public bool OnPressed(PlatformAction action) - { - if (action.ActionType == PlatformActionType.Copy) - host.GetClipboard().SetText(formatSelectionAsString()); - - return false; - } - - public void OnReleased(PlatformAction action) - { - } + #endregion } } From 3909eda095ce5f6fcfdc7afa7e3ea4fdaf826540 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 18:51:28 +0900 Subject: [PATCH 56/94] Avoid using a StringBuilder --- osu.Game/Screens/Edit/Compose/ComposeScreen.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 1d52cc60fd..61056aeced 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Linq; -using System.Text; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -101,15 +100,15 @@ namespace osu.Game.Screens.Edit.Compose private string formatSelectionAsString() { - var builder = new StringBuilder(); - builder.Append(EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).FirstOrDefault()?.StartTime.ToEditorFormattedString() ?? clock.CurrentTime.ToEditorFormattedString()); + if (composer == null) + return string.Empty; - if (EditorBeatmap.SelectedHitObjects.Any() && composer != null) - builder.Append($" ({composer.ConvertSelectionToString()})"); + double displayTime = EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).FirstOrDefault()?.StartTime ?? clock.CurrentTime; + string selectionAsString = composer.ConvertSelectionToString(); - builder.Append(" - "); - - return builder.ToString(); + return !string.IsNullOrEmpty(selectionAsString) + ? $"{displayTime.ToEditorFormattedString()} ({selectionAsString}) - " + : $"{displayTime.ToEditorFormattedString()} - "; } #endregion From 1d99a63f17f8e339097552dc51fbc27bef00b5c7 Mon Sep 17 00:00:00 2001 From: Leon Gebler Date: Mon, 29 Mar 2021 14:02:53 +0200 Subject: [PATCH 57/94] 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 58/94] 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 59/94] 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 36364a449249fb42289e82e1e8d6c05105a43885 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 22:12:49 +0900 Subject: [PATCH 60/94] Update framework --- osu.Android.props | 2 +- osu.Game/Input/Handlers/ReplayInputHandler.cs | 2 -- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 75ac298626..3682a44b9f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index 93ed3ca884..fba1bee0b8 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -34,8 +34,6 @@ namespace osu.Game.Input.Handlers public override bool IsActive => true; - public override int Priority => 0; - public class ReplayState : IInput where T : struct { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b90c938a8b..35b0827715 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ce182a3054..ceb46eae87 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From d84c9251e6d4d6569e5f895a7cf6943c2c722467 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 22:17:24 +0900 Subject: [PATCH 61/94] Update nuget packages --- osu.Game/osu.Game.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 35b0827715..6d571218fc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,20 +18,20 @@ - + - - - + + + - + - + From 6c5a10a7449a24fdd6932c6cb8ba99c7d98affd6 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 29 Mar 2021 15:27:25 +0200 Subject: [PATCH 62/94] 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 6f01070408fb5fad37d33765a22997d1189d9cc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 Mar 2021 23:06:29 +0900 Subject: [PATCH 63/94] Add weird android package requirements --- osu.Android/osu.Android.csproj | 5 +++++ .../osu.Game.Rulesets.Catch.Tests.Android.csproj | 5 +++++ .../osu.Game.Rulesets.Mania.Tests.Android.csproj | 5 +++++ .../osu.Game.Rulesets.Osu.Tests.Android.csproj | 5 +++++ .../osu.Game.Rulesets.Taiko.Tests.Android.csproj | 5 +++++ osu.Game.Tests.Android/osu.Game.Tests.Android.csproj | 3 +++ 6 files changed, 28 insertions(+) diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index a2638e95c8..2051beae21 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -53,5 +53,10 @@ + + + 5.0.0 + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj index 88b420ffad..2e6c10a02e 100644 --- a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj +++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj @@ -35,5 +35,10 @@ osu.Game + + + 5.0.0 + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj index 0e557cb260..8c134c7114 100644 --- a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj +++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj @@ -35,5 +35,10 @@ osu.Game + + + 5.0.0 + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj index dcf1573522..22fa605176 100644 --- a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj +++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj @@ -35,5 +35,10 @@ osu.Game + + + 5.0.0 + + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj index 392442b713..a48110b354 100644 --- a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj +++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj @@ -35,5 +35,10 @@ osu.Game + + + 5.0.0 + + \ No newline at end of file diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index c3d9cb5875..bf256f486c 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -75,6 +75,9 @@ + + 5.0.0 + From 2d344ae6ffd9a5dee8e47096060a1c4f949b677e Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 29 Mar 2021 16:16:50 +0200 Subject: [PATCH 64/94] 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 804ffe9f48954378ef688bd9d3d58120d0ccbf21 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Mar 2021 09:00:09 +0900 Subject: [PATCH 65/94] Fix inspections --- .../Online/TestAPIModJsonSerialization.cs | 22 +++++++++---------- .../Screens/Editors/TeamEditorScreen.cs | 8 +++++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index ab24a72a12..77f910c144 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Online var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); - Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym)); + Assert.That(deserialized?.Acronym, Is.EqualTo(apiMod.Acronym)); } [Test] @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Online var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); - Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0)); + Assert.That(deserialized?.Settings, Contains.Key("test_setting").With.ContainValue(2.0)); } [Test] @@ -44,9 +44,9 @@ namespace osu.Game.Tests.Online var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } }); var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); - var converted = (TestMod)deserialized.ToMod(new TestRuleset()); + var converted = (TestMod)deserialized?.ToMod(new TestRuleset()); - Assert.That(converted.TestSetting.Value, Is.EqualTo(2)); + Assert.That(converted?.TestSetting.Value, Is.EqualTo(2)); } [Test] @@ -61,11 +61,11 @@ namespace osu.Game.Tests.Online }); var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); - var converted = (TestModTimeRamp)deserialised.ToMod(new TestRuleset()); + var converted = (TestModTimeRamp)deserialised?.ToMod(new TestRuleset()); - Assert.That(converted.AdjustPitch.Value, Is.EqualTo(false)); - Assert.That(converted.InitialRate.Value, Is.EqualTo(1.25)); - Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25)); + Assert.That(converted?.AdjustPitch.Value, Is.EqualTo(false)); + Assert.That(converted?.InitialRate.Value, Is.EqualTo(1.25)); + Assert.That(converted?.FinalRate.Value, Is.EqualTo(0.25)); } [Test] @@ -78,10 +78,10 @@ namespace osu.Game.Tests.Online }); var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(apiMod)); - var converted = (TestModDifficultyAdjust)deserialised.ToMod(new TestRuleset()); + var converted = (TestModDifficultyAdjust)deserialised?.ToMod(new TestRuleset()); - Assert.That(converted.ExtendedLimits.Value, Is.True); - Assert.That(converted.OverallDifficulty.Value, Is.EqualTo(11)); + Assert.That(converted?.ExtendedLimits.Value, Is.True); + Assert.That(converted?.OverallDifficulty.Value, Is.EqualTo(11)); } private class TestRuleset : Ruleset diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index 582f72429b..f051823541 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -43,12 +43,16 @@ namespace osu.Game.Tournament.Screens.Editors private void addAllCountries() { List countries; + using (Stream stream = game.Resources.GetStream("Resources/countries.json")) using (var sr = new StreamReader(stream)) countries = JsonConvert.DeserializeObject>(sr.ReadToEnd()); - foreach (var c in countries) - Storage.Add(c); + if (countries != null) + { + foreach (var c in countries) + Storage.Add(c); + } } public class TeamRow : CompositeDrawable, IModelBacked From 69db0a55938176ca9ad4b67cb2922c00d896f934 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Mar 2021 09:03:32 +0900 Subject: [PATCH 66/94] Countries should not be null (internal game resource) --- .../Screens/Editors/TeamEditorScreen.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs index f051823541..aa1be143ea 100644 --- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using Newtonsoft.Json; @@ -48,11 +49,10 @@ namespace osu.Game.Tournament.Screens.Editors using (var sr = new StreamReader(stream)) countries = JsonConvert.DeserializeObject>(sr.ReadToEnd()); - if (countries != null) - { - foreach (var c in countries) - Storage.Add(c); - } + Debug.Assert(countries != null); + + foreach (var c in countries) + Storage.Add(c); } public class TeamRow : CompositeDrawable, IModelBacked From 8a0fcf20ede3a63a074072899b33cb02cd83fdec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 12:32:42 +0900 Subject: [PATCH 67/94] Move offset settings up for more logical ordering --- .../Settings/Sections/Input/TabletSettings.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index bd0f7ddc4c..571d79baf8 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -110,12 +110,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input } }, new SettingsSlider - { - TransferValueOnCommit = true, - LabelText = "Aspect Ratio", - Current = aspectRatio - }, - new SettingsSlider { TransferValueOnCommit = true, LabelText = "X Offset", @@ -127,6 +121,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Y Offset", Current = offsetY }, + new SettingsSlider + { + TransferValueOnCommit = true, + LabelText = "Aspect Ratio", + Current = aspectRatio + }, new SettingsCheckbox { LabelText = "Lock aspect ratio", From 8dfff999f9ced7f2e550df44fcf3c76e871055fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 12:40:50 +0900 Subject: [PATCH 68/94] Add rotation slider --- .../Visual/Settings/TestSceneTabletSettings.cs | 2 ++ .../Settings/Sections/Input/TabletAreaSelection.cs | 9 +++++++++ .../Overlays/Settings/Sections/Input/TabletSettings.cs | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs index a7f6c8c0d3..a62980addf 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs @@ -45,6 +45,8 @@ namespace osu.Game.Tests.Visual.Settings public Bindable AreaOffset { get; } = new Bindable(); public Bindable AreaSize { get; } = new Bindable(); + public Bindable Rotation { get; } = new Bindable(); + public IBindable Tablet => tablet; private readonly Bindable tablet = new Bindable(); diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs index ecb8acce54..f61742093c 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs @@ -25,6 +25,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly Bindable areaOffset = new Bindable(); private readonly Bindable areaSize = new Bindable(); + private readonly BindableNumber rotation = new BindableNumber(); + private readonly IBindable tablet = new Bindable(); private OsuSpriteText tabletName; @@ -124,6 +126,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input usableAreaText.Text = $"{(float)x / commonDivider}:{(float)y / commonDivider}"; }, true); + rotation.BindTo(handler.Rotation); + rotation.BindValueChanged(val => + { + usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint) + .OnComplete(_ => checkBounds()); // required as we are using SSDQ. + }); + tablet.BindTo(handler.Tablet); tablet.BindValueChanged(_ => Scheduler.AddOnce(updateTabletDetails)); diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 571d79baf8..9d128f8db3 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -27,6 +27,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input private readonly BindableNumber sizeX = new BindableNumber { MinValue = 10 }; private readonly BindableNumber sizeY = new BindableNumber { MinValue = 10 }; + private readonly BindableNumber rotation = new BindableNumber { MinValue = 0, MaxValue = 360 }; + [Resolved] private GameHost host { get; set; } @@ -122,6 +124,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input Current = offsetY }, new SettingsSlider + { + TransferValueOnCommit = true, + LabelText = "Rotation", + Current = rotation + }, + new SettingsSlider { TransferValueOnCommit = true, LabelText = "Aspect Ratio", @@ -153,6 +161,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { base.LoadComplete(); + rotation.BindTo(tabletHandler.Rotation); + areaOffset.BindTo(tabletHandler.AreaOffset); areaOffset.BindValueChanged(val => { From 1dfd08eded521c40576cc746216f3b487e239e9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 13:01:48 +0900 Subject: [PATCH 69/94] Add tablet rotation configuration --- .../Sections/Input/RotationPresetButtons.cs | 109 ++++++++++++++++++ .../Settings/Sections/Input/TabletSettings.cs | 1 + 2 files changed, 110 insertions(+) create mode 100644 osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs diff --git a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs new file mode 100644 index 0000000000..3e8da9f7d0 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs @@ -0,0 +1,109 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Handlers.Tablet; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings.Sections.Input +{ + internal class RotationPresetButtons : FillFlowContainer + { + private readonly ITabletHandler tabletHandler; + + private Bindable rotation; + + private const int height = 50; + + public RotationPresetButtons(ITabletHandler tabletHandler) + { + this.tabletHandler = tabletHandler; + + RelativeSizeAxes = Axes.X; + Height = height; + + for (int i = 0; i < 360; i += 90) + { + var presetRotation = i; + + Add(new RotationButton(i) + { + RelativeSizeAxes = Axes.X, + Height = height, + Width = 0.25f, + Text = $"{presetRotation}ยบ", + Action = () => tabletHandler.Rotation.Value = presetRotation, + }); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + rotation = tabletHandler.Rotation.GetBoundCopy(); + rotation.BindValueChanged(val => + { + foreach (var b in Children.OfType()) + b.IsSelected = b.Preset == val.NewValue; + }, true); + } + + public class RotationButton : TriangleButton + { + [Resolved] + private OsuColour colours { get; set; } + + public readonly int Preset; + + public RotationButton(int preset) + { + Preset = preset; + } + + private bool isSelected; + + public bool IsSelected + { + get => isSelected; + set + { + if (value == isSelected) + return; + + isSelected = value; + + if (IsLoaded) + updateColour(); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateColour(); + } + + private void updateColour() + { + if (isSelected) + { + BackgroundColour = colours.BlueDark; + Triangles.ColourDark = colours.BlueDarker; + Triangles.ColourLight = colours.Blue; + } + else + { + BackgroundColour = colours.Gray4; + Triangles.ColourDark = colours.Gray5; + Triangles.ColourLight = colours.Gray6; + } + } + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 9d128f8db3..d770c18878 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -129,6 +129,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input LabelText = "Rotation", Current = rotation }, + new RotationPresetButtons(tabletHandler), new SettingsSlider { TransferValueOnCommit = true, From b82247aabec2ee10c12105701d1c0e073a775e6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 14:13:16 +0900 Subject: [PATCH 70/94] 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 71/94] 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 72/94] 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 73/94] 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 90c75a64cf9e30e213821706e8d54de262d2b629 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 15:24:11 +0900 Subject: [PATCH 74/94] Fix legacy control point precision having an adverse effect on the editor --- osu.Game/Screens/Edit/Timing/DifficultySection.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs index 9d80ca0b14..97d110c502 100644 --- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs @@ -28,7 +28,16 @@ namespace osu.Game.Screens.Edit.Timing { if (point.NewValue != null) { - multiplierSlider.Current = point.NewValue.SpeedMultiplierBindable; + var selectedPointBindable = point.NewValue.SpeedMultiplierBindable; + + // there may be legacy control points, which contain infinite precision for compatibility reasons (see LegacyDifficultyControlPoint). + // generally that level of precision could only be set by externally editing the .osu file, so at the point + // a user is looking to update this within the editor it should be safe to obliterate this additional precision. + double expectedPrecision = new DifficultyControlPoint().SpeedMultiplierBindable.Precision; + if (selectedPointBindable.Precision < expectedPrecision) + selectedPointBindable.Precision = expectedPrecision; + + multiplierSlider.Current = selectedPointBindable; multiplierSlider.Current.BindValueChanged(_ => ChangeHandler?.SaveState()); } } From 1d968009c2c329a1abd107409c9e37c302ae36c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 16:07:04 +0900 Subject: [PATCH 75/94] 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 76/94] 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 77/94] 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); From 633e6130bf434c628d7be98eeba0f0fff9d30cf8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 19:45:22 +0900 Subject: [PATCH 78/94] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3682a44b9f..5b65670869 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6d571218fc..6a7f7e7026 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ceb46eae87..4aa3ad1c61 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From fb0079fb9f641c7a7c769a3144684cb90c0320a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 22:42:31 +0900 Subject: [PATCH 79/94] Fix accuracy displaying incorrectly in online contexts Closes #12221. --- osu.Game/Users/UserStatistics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 04a358436e..5ddcd86d28 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -45,7 +45,7 @@ namespace osu.Game.Users public double Accuracy; [JsonIgnore] - public string DisplayAccuracy => Accuracy.FormatAccuracy(); + public string DisplayAccuracy => (Accuracy / 100).FormatAccuracy(); [JsonProperty(@"play_count")] public int PlayCount; From ded91b32a4facc2e55d909428999401ffec0ac80 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Mar 2021 12:11:43 +0900 Subject: [PATCH 80/94] Add failing test --- .../TestSceneHoldNoteInput.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 7ae69bf7d7..2357778948 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -288,6 +288,42 @@ namespace osu.Game.Rulesets.Mania.Tests .All(j => j.Type.IsHit())); } + [Test] + public void TestHitTailBeforeLastTick() + { + const int tick_rate = 8; + const double tick_spacing = TimingControlPoint.DEFAULT_BEAT_LENGTH / tick_rate; + const double time_last_tick = time_head + tick_spacing * (int)((time_tail - time_head) / tick_spacing - 1); + + var beatmap = new Beatmap + { + HitObjects = + { + new HoldNote + { + StartTime = time_head, + Duration = time_tail - time_head, + Column = 0, + } + }, + BeatmapInfo = + { + BaseDifficulty = new BeatmapDifficulty { SliderTickRate = tick_rate }, + Ruleset = new ManiaRuleset().RulesetInfo + }, + }; + + performTest(new List + { + new ManiaReplayFrame(time_head, ManiaAction.Key1), + new ManiaReplayFrame(time_last_tick - 5) + }, beatmap); + + assertHeadJudgement(HitResult.Perfect); + AddAssert("one tick missed", () => judgementResults.Where(j => j.HitObject is HoldNoteTick).Count(j => j.Type == HitResult.LargeTickMiss) == 1); + assertTailJudgement(HitResult.Ok); + } + private void assertHeadJudgement(HitResult result) => AddAssert($"head judged as {result}", () => judgementResults[0].Type == result); From f78d628878a0244c1de1b0efa26d9e206d593771 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Mar 2021 12:21:07 +0900 Subject: [PATCH 81/94] Improve assertions --- .../TestSceneHoldNoteInput.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 2357778948..42ea12214f 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -320,21 +320,24 @@ namespace osu.Game.Rulesets.Mania.Tests }, beatmap); assertHeadJudgement(HitResult.Perfect); - AddAssert("one tick missed", () => judgementResults.Where(j => j.HitObject is HoldNoteTick).Count(j => j.Type == HitResult.LargeTickMiss) == 1); + assertLastTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.Ok); } private void assertHeadJudgement(HitResult result) - => AddAssert($"head judged as {result}", () => judgementResults[0].Type == result); + => AddAssert($"head judged as {result}", () => judgementResults.First(j => j.HitObject is Note).Type == result); private void assertTailJudgement(HitResult result) - => AddAssert($"tail judged as {result}", () => judgementResults[^2].Type == result); + => AddAssert($"tail judged as {result}", () => judgementResults.Single(j => j.HitObject is TailNote).Type == result); private void assertNoteJudgement(HitResult result) - => AddAssert($"hold note judged as {result}", () => judgementResults[^1].Type == result); + => AddAssert($"hold note judged as {result}", () => judgementResults.Single(j => j.HitObject is HoldNote).Type == result); private void assertTickJudgement(HitResult result) - => AddAssert($"tick judged as {result}", () => judgementResults[6].Type == result); // arbitrary tick + => AddAssert($"any tick judged as {result}", () => judgementResults.Where(j => j.HitObject is HoldNoteTick).Any(j => j.Type == result)); + + private void assertLastTickJudgement(HitResult result) + => AddAssert($"last tick judged as {result}", () => judgementResults.Last(j => j.HitObject is HoldNoteTick).Type == result); private ScoreAccessibleReplayPlayer currentPlayer; From 43e48406caa2253ccdc580c53ad22d0b39ad7ebf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 31 Mar 2021 12:21:14 +0900 Subject: [PATCH 82/94] Miss all ticks when hold note is hit --- .../Objects/Drawables/DrawableHoldNote.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 4f062753a6..828ee7b03e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -233,6 +233,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (Tail.AllJudged) { + foreach (var tick in tickContainer) + { + if (!tick.Judged) + tick.MissForcefully(); + } + ApplyResult(r => r.Type = r.Judgement.MaxResult); endHold(); } From e0c61f4dc556189de3e841242568df93269aafdd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 31 Mar 2021 13:57:57 +0900 Subject: [PATCH 83/94] Fix retry count not updating correctly Regressed with changes to player reference retention logic. Could add a test but the logic is so local now it seems quite redundant. --- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 7d906cdc5b..2bbc4a0469 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -309,10 +309,8 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - var restartCount = player?.RestartCount + 1 ?? 0; - player = createPlayer(); - player.RestartCount = restartCount; + player.RestartCount = ++restartCount; player.RestartRequested = restartRequested; LoadTask = LoadComponentAsync(player, _ => MetadataInfo.Loading = false); @@ -428,6 +426,8 @@ namespace osu.Game.Screens.Play private Bindable muteWarningShownOnce; + private int restartCount; + private void showMuteWarningIfNeeded() { if (!muteWarningShownOnce.Value) From 0c53b4eb938a2770c5b2420dcf646cce0bd16c23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 31 Mar 2021 14:09:38 +0900 Subject: [PATCH 84/94] Fix wrong counting and add test --- .../Navigation/TestSceneScreenNavigation.cs | 23 +++++++++++++++++++ osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index f2bb518b2e..3e25e22b5f 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -43,6 +43,29 @@ namespace osu.Game.Tests.Visual.Navigation exitViaEscapeAndConfirm(); } + [Test] + public void TestRetryCountIncrements() + { + Player player = null; + + PushAndConfirm(() => new TestSongSelect()); + + AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); + + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + AddAssert("retry count is 0", () => player.RestartCount == 0); + + AddStep("attempt to retry", () => player.ChildrenOfType().First().Action()); + AddUntilStep("wait for old player gone", () => Game.ScreenStack.CurrentScreen != player); + + AddUntilStep("get new player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + AddAssert("retry count is 1", () => player.RestartCount == 1); + } + [Test] public void TestRetryFromResults() { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 2bbc4a0469..679b3c7313 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play return; player = createPlayer(); - player.RestartCount = ++restartCount; + player.RestartCount = restartCount++; player.RestartRequested = restartRequested; LoadTask = LoadComponentAsync(player, _ => MetadataInfo.Loading = false); From 30cae46cbdf44021e850f63122d90d803052ca9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 31 Mar 2021 14:57:28 +0900 Subject: [PATCH 85/94] Group large drag drop imports into a single operation --- osu.Desktop/OsuGameDesktop.cs | 34 +++++++++++++++++++++++++++++++--- osu.Game/OsuGameBase.cs | 3 +++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b2487568ce..0c21c75290 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -2,6 +2,7 @@ // 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 System.Reflection; @@ -18,6 +19,7 @@ using osu.Framework.Screens; using osu.Game.Screens.Menu; using osu.Game.Updater; using osu.Desktop.Windows; +using osu.Framework.Threading; using osu.Game.IO; namespace osu.Desktop @@ -144,13 +146,39 @@ namespace osu.Desktop desktopWindow.DragDrop += f => fileDrop(new[] { f }); } + private readonly List importableFiles = new List(); + private ScheduledDelegate importSchedule; + private void fileDrop(string[] filePaths) { - var firstExtension = Path.GetExtension(filePaths.First()); + lock (importableFiles) + { + var firstExtension = Path.GetExtension(filePaths.First()); - if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; + if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; - Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); + importableFiles.AddRange(filePaths); + + Logger.Log($"Adding {filePaths.Length} files for import"); + + // File drag drop operations can potentially trigger hundreds or thousands of these calls on some platforms. + // In order to avoid spawning multiple import tasks for a single drop operation, debounce a touch. + importSchedule?.Cancel(); + importSchedule = Scheduler.AddDelayed(handlePendingImports, 100); + } + } + + private void handlePendingImports() + { + lock (importableFiles) + { + Logger.Log($"Handling batch import of {importableFiles.Count} files"); + + var paths = importableFiles.ToArray(); + importableFiles.Clear(); + + Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning); + } } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e1c7b67a8c..7eef0f7158 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -429,6 +429,9 @@ namespace osu.Game public async Task Import(params string[] paths) { + if (paths.Length == 0) + return; + var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); foreach (var importer in fileImporters) From 6a286c5e2128c3103bd36548434f29260aec806d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 1 Apr 2021 17:16:02 +0000 Subject: [PATCH 86/94] Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.9.1 to 16.9.4. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.9.1...v16.9.4) Signed-off-by: dependabot-preview[bot] --- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 728af5124e..c2d9a923d9 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index af16f39563..64e934efd2 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 3d2d1f3fec..f743d65db3 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index fa00922706..eab144592f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e36b3cdc74..0e1f6f6b0c 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index b20583dd7e..a4e52f8cd4 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From fcd56dba44a84e3e752e97f0eb3ed6409407f5ac Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 2 Apr 2021 01:38:10 +0300 Subject: [PATCH 87/94] Guard against same ruleset file with loaded assembly filenames instead --- osu.Game/Rulesets/RulesetStore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index deabea57ef..eb5271aa17 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -173,7 +174,7 @@ namespace osu.Game.Rulesets { var filename = Path.GetFileNameWithoutExtension(file); - if (loadedAssemblies.Values.Any(t => t.Namespace == filename)) + if (loadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename)) return; try From 5b1dc7d2b426e505da4b67cf8876404078a7f72f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 2 Apr 2021 02:45:26 +0300 Subject: [PATCH 88/94] Remove unused using directive --- osu.Game/Rulesets/RulesetStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index eb5271aa17..4261ee3d47 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; From e1aa9278272eabe7f6a975f3c78f4c03fca9122c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Apr 2021 13:20:15 +0900 Subject: [PATCH 89/94] Add dropdown option to export score --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 5608002513..da1bbd18c7 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -61,6 +61,9 @@ namespace osu.Game.Online.Leaderboards [Resolved(CanBeNull = true)] private SongSelect songSelect { get; set; } + [Resolved] + private ScoreManager scoreManager { get; set; } + public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true) { this.score = score; @@ -388,6 +391,9 @@ namespace osu.Game.Online.Leaderboards if (score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null) items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = score.Mods)); + if (score.Files.Count > 0) + items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(score))); + if (score.ID != 0) items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score)))); From 6d4d574a659c5148a7400003c6896990b2b689aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 2 Apr 2021 14:10:25 +0900 Subject: [PATCH 90/94] Fix exported replay filenames not having full metadata --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index a898e10e4f..bf7906bd5c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -147,7 +147,7 @@ namespace osu.Game.Beatmaps { string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; - return $"{Metadata} {version}".Trim(); + return $"{Metadata ?? BeatmapSet?.Metadata} {version}".Trim(); } public bool Equals(BeatmapInfo other) From 5063cd957f8013c060aff0d5bc14b6a4ec58a9e1 Mon Sep 17 00:00:00 2001 From: Amber Date: Fri, 2 Apr 2021 02:54:35 -0500 Subject: [PATCH 91/94] Force hit sample to play when Classic mod is enabled --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 4 ++++ osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/DrawableSliderTail.cs | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 5470d0fcb4..21fde7cdc7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -79,6 +79,10 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSliderHead head: head.TrackFollowCircle = !NoSliderHeadMovement.Value; break; + + case DrawableSliderTail tail: + tail.AlwaysPlaySample = true; + break; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 9122f347d0..4288f00e3a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -280,7 +280,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { // rather than doing it this way, we should probably attach the sample to the tail circle. // this can only be done after we stop using LegacyLastTick. - if (TailCircle.IsHit) + if (TailCircle.IsHit || TailCircle.AlwaysPlaySample) base.PlaySamples(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 6a8e02e886..ad6b98cba8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -26,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public override bool DisplayResult => false; + /// + /// Whether the hit sample should always be played, regardless of whether the tail was actually hit. + /// + public bool AlwaysPlaySample { get; set; } + public bool Tracking { get; set; } private SkinnableDrawable circlePiece; @@ -44,6 +49,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load() { + AlwaysPlaySample = false; Origin = Anchor.Centre; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); From 48e9985782cb5537b1053d6beb05342409a7a921 Mon Sep 17 00:00:00 2001 From: Amber Date: Fri, 2 Apr 2021 03:10:28 -0500 Subject: [PATCH 92/94] Make "AlwaysPlayTailSample" a mod setting rather than a hardcoded constant. --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 21fde7cdc7..ec74225774 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -44,6 +44,9 @@ namespace osu.Game.Rulesets.Osu.Mods [SettingSource("Use fixed slider follow circle hit area", "Makes the slider follow circle track its final size at all times.")] public Bindable FixedFollowCircleHitArea { get; } = new BindableBool(true); + [SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")] + public Bindable AlwaysPlayTailSample { get; } = new BindableBool(true); + public void ApplyToHitObject(HitObject hitObject) { switch (hitObject) @@ -81,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; case DrawableSliderTail tail: - tail.AlwaysPlaySample = true; + tail.AlwaysPlaySample = AlwaysPlayTailSample.Value; break; } } From 5ac36a24625eb80c5f897ed7929788e08bb988c4 Mon Sep 17 00:00:00 2001 From: Amber Date: Fri, 2 Apr 2021 03:56:23 -0500 Subject: [PATCH 93/94] Switch AlwaysPlaySample to SamplePlaysOnlyOnHit in DrawableSliderTail for conformity --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index ec74225774..882f848190 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; case DrawableSliderTail tail: - tail.AlwaysPlaySample = AlwaysPlayTailSample.Value; + tail.SamplePlaysOnlyOnHit = !AlwaysPlayTailSample.Value; break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4288f00e3a..04708a5ece 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -280,7 +280,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { // rather than doing it this way, we should probably attach the sample to the tail circle. // this can only be done after we stop using LegacyLastTick. - if (TailCircle.IsHit || TailCircle.AlwaysPlaySample) + if (!TailCircle.SamplePlaysOnlyOnHit || TailCircle.IsHit) base.PlaySamples(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index ad6b98cba8..4b9c10ab67 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Whether the hit sample should always be played, regardless of whether the tail was actually hit. /// - public bool AlwaysPlaySample { get; set; } + public bool SamplePlaysOnlyOnHit { get; set; } = true; public bool Tracking { get; set; } @@ -49,7 +49,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AlwaysPlaySample = false; Origin = Anchor.Centre; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); From 9c3d15171c6ba033970550e6e158a3fce750c3f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Apr 2021 18:00:28 +0900 Subject: [PATCH 94/94] Reword xmldoc slightly --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 4b9c10ab67..87f098dd29 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool DisplayResult => false; /// - /// Whether the hit sample should always be played, regardless of whether the tail was actually hit. + /// Whether the hit samples only play on successful hits. + /// If false, the hit samples will also play on misses. /// public bool SamplePlaysOnlyOnHit { get; set; } = true;