From f116854c9fed7d6318ceb4021e0e99a62fbe58a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= <dach.bartlomiej@gmail.com>
Date: Tue, 17 Oct 2023 14:15:17 +0200
Subject: [PATCH 1/3] Add test step for covering slider appearance with hidden
 active

---
 osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index e005d7cac3..b805e7ed63 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -27,6 +27,7 @@ using osu.Game.Rulesets.Objects;
 using osu.Game.Rulesets.Objects.Drawables;
 using osu.Game.Rulesets.Objects.Types;
 using osu.Game.Rulesets.Osu.Configuration;
+using osu.Game.Rulesets.Osu.Mods;
 
 namespace osu.Game.Rulesets.Osu.Tests
 {
@@ -50,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.Tests
                 snakingOut.Value = !v;
             });
 
+            AddToggleStep("toggle hidden", hiddenActive => SelectedMods.Value = hiddenActive ? new[] { new OsuModHidden() } : Array.Empty<Mod>());
+
             AddSliderStep("hit at", 0f, 1f, 0f, v =>
             {
                 progressToHit = v;

From 30e5f470073399be071a7e947f6331c8056d3770 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= <dach.bartlomiej@gmail.com>
Date: Tue, 17 Oct 2023 13:39:52 +0200
Subject: [PATCH 2/3] Instantly fade out slider repeats when hidden is active

---
 osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index 996ee1cddb..dd2befef4e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -98,6 +98,9 @@ namespace osu.Game.Rulesets.Osu.Mods
                         // only apply to circle piece – reverse arrow is not affected by hidden.
                         sliderRepeat.CirclePiece.FadeOut(fadeDuration);
 
+                    using (drawableObject.BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
+                        sliderRepeat.FadeOut();
+
                     break;
 
                 case DrawableHitCircle circle:

From f3cda5847448fa037b8739cf28815ebda4be0c49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= <dach.bartlomiej@gmail.com>
Date: Tue, 17 Oct 2023 14:33:57 +0200
Subject: [PATCH 3/3] Fix legacy slider repeats becoming much too large on hit

They had scale transforms applied to them in two places: the actual
legacy pieces themselves (esp. `LegacyHitCirclePiece`), and on the
`DrawableSliderRepeat` level.

This change moves all of the scale transforms to the skinnable pieces.
Argon and triangles have received a copy of the previous logic each,
so behaviour on those skins should not change.
---
 .../Objects/Drawables/DrawableSliderRepeat.cs |  5 ----
 .../Skinning/Argon/ArgonReverseArrow.cs       | 22 ++++++++++-----
 .../Skinning/Default/DefaultReverseArrow.cs   | 19 +++++++++----
 .../Skinning/Legacy/LegacyReverseArrow.cs     | 28 +++++++++++--------
 4 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index ac4d733672..0be571c229 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -118,11 +118,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
 
                 case ArmedState.Hit:
                     this.FadeOut(animDuration, Easing.Out);
-
-                    const float final_scale = 1.5f;
-
-                    Arrow.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
-                    CirclePiece.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
                     break;
             }
         }
diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonReverseArrow.cs
index 160edb6f67..87b89a07cf 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonReverseArrow.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonReverseArrow.cs
@@ -1,6 +1,7 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System;
 using osu.Framework.Allocation;
 using osu.Framework.Bindables;
 using osu.Framework.Extensions.Color4Extensions;
@@ -12,6 +13,7 @@ using osu.Framework.Graphics.Sprites;
 using osu.Framework.Graphics.Textures;
 using osu.Game.Rulesets.Objects.Drawables;
 using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
 using osuTK;
 using osuTK.Graphics;
 
@@ -19,8 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
 {
     public partial class ArgonReverseArrow : CompositeDrawable
     {
-        [Resolved]
-        private DrawableHitObject drawableObject { get; set; } = null!;
+        private DrawableSliderRepeat drawableRepeat { get; set; } = null!;
 
         private Bindable<Color4> accentColour = null!;
 
@@ -29,8 +30,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
         private Sprite side = null!;
 
         [BackgroundDependencyLoader]
-        private void load(TextureStore textures)
+        private void load(DrawableHitObject drawableObject, TextureStore textures)
         {
+            drawableRepeat = (DrawableSliderRepeat)drawableObject;
+
             Anchor = Anchor.Centre;
             Origin = Anchor.Centre;
 
@@ -70,10 +73,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
                 }
             };
 
-            accentColour = drawableObject.AccentColour.GetBoundCopy();
+            accentColour = drawableRepeat.AccentColour.GetBoundCopy();
             accentColour.BindValueChanged(accent => icon.Colour = accent.NewValue.Darken(4), true);
 
-            drawableObject.ApplyCustomUpdateState += updateStateTransforms;
+            drawableRepeat.ApplyCustomUpdateState += updateStateTransforms;
         }
 
         private void updateStateTransforms(DrawableHitObject hitObject, ArmedState state)
@@ -96,6 +99,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
                         .MoveToX(0, move_in_duration, Easing.Out)
                         .Loop(total - (move_in_duration + move_out_duration));
                     break;
+
+                case ArmedState.Hit:
+                    double animDuration = Math.Min(300, drawableRepeat.HitObject.SpanDuration);
+                    this.ScaleTo(1.5f, animDuration, Easing.Out);
+                    break;
             }
         }
 
@@ -103,8 +111,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
         {
             base.Dispose(isDisposing);
 
-            if (drawableObject.IsNotNull())
-                drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
+            if (drawableRepeat.IsNotNull())
+                drawableRepeat.ApplyCustomUpdateState -= updateStateTransforms;
         }
     }
 }
diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultReverseArrow.cs
index b44f6571b9..ad49150d81 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultReverseArrow.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultReverseArrow.cs
@@ -1,6 +1,7 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System;
 using osu.Framework.Allocation;
 using osu.Framework.Extensions.ObjectExtensions;
 using osu.Framework.Graphics;
@@ -8,14 +9,14 @@ using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Sprites;
 using osu.Game.Rulesets.Objects.Drawables;
 using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
 using osuTK;
 
 namespace osu.Game.Rulesets.Osu.Skinning.Default
 {
     public partial class DefaultReverseArrow : CompositeDrawable
     {
-        [Resolved]
-        private DrawableHitObject drawableObject { get; set; } = null!;
+        private DrawableSliderRepeat drawableRepeat { get; set; } = null!;
 
         public DefaultReverseArrow()
         {
@@ -36,9 +37,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
         }
 
         [BackgroundDependencyLoader]
-        private void load()
+        private void load(DrawableHitObject drawableObject)
         {
-            drawableObject.ApplyCustomUpdateState += updateStateTransforms;
+            drawableRepeat = (DrawableSliderRepeat)drawableObject;
+            drawableRepeat.ApplyCustomUpdateState += updateStateTransforms;
         }
 
         private void updateStateTransforms(DrawableHitObject hitObject, ArmedState state)
@@ -55,6 +57,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
                                  .ScaleTo(1f, move_in_duration, Easing.Out)
                                  .Loop(total - (move_in_duration + move_out_duration));
                     break;
+
+                case ArmedState.Hit:
+                    double animDuration = Math.Min(300, drawableRepeat.HitObject.SpanDuration);
+                    InternalChild.ScaleTo(1.5f, animDuration, Easing.Out);
+                    break;
             }
         }
 
@@ -62,8 +69,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
         {
             base.Dispose(isDisposing);
 
-            if (drawableObject.IsNotNull())
-                drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
+            if (drawableRepeat.IsNotNull())
+                drawableRepeat.ApplyCustomUpdateState -= updateStateTransforms;
         }
     }
 }
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs
index 9048a92e13..a535fbdbc3 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs
@@ -1,6 +1,7 @@
 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
 // See the LICENCE file in the repository root for full licence text.
 
+using System;
 using System.Diagnostics;
 using osu.Framework.Allocation;
 using osu.Framework.Bindables;
@@ -17,8 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
 {
     public partial class LegacyReverseArrow : CompositeDrawable
     {
-        [Resolved]
-        private DrawableHitObject drawableObject { get; set; } = null!;
+        private DrawableSliderRepeat drawableRepeat { get; set; } = null!;
 
         private Drawable proxy = null!;
 
@@ -31,8 +31,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
         private bool shouldRotate;
 
         [BackgroundDependencyLoader]
-        private void load(ISkinSource skinSource)
+        private void load(DrawableHitObject drawableObject, ISkinSource skinSource)
         {
+            drawableRepeat = (DrawableSliderRepeat)drawableObject;
+
             AutoSizeAxes = Axes.Both;
 
             string lookupName = new OsuSkinComponentLookup(OsuSkinComponents.ReverseArrow).LookupName;
@@ -58,10 +60,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
 
             proxy = CreateProxy();
 
-            drawableObject.HitObjectApplied += onHitObjectApplied;
-            onHitObjectApplied(drawableObject);
+            drawableRepeat.HitObjectApplied += onHitObjectApplied;
+            onHitObjectApplied(drawableRepeat);
 
-            accentColour = drawableObject.AccentColour.GetBoundCopy();
+            accentColour = drawableRepeat.AccentColour.GetBoundCopy();
             accentColour.BindValueChanged(c =>
             {
                 arrow.Colour = textureIsDefaultSkin && c.NewValue.R + c.NewValue.G + c.NewValue.B > (600 / 255f) ? Color4.Black : Color4.White;
@@ -73,8 +75,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
             Debug.Assert(proxy.Parent == null);
 
             // see logic in LegacySliderHeadHitCircle.
-            (drawableObject as DrawableSliderRepeat)?.DrawableSlider
-                                                    .OverlayElementContainer.Add(proxy);
+            drawableRepeat.DrawableSlider.OverlayElementContainer.Add(proxy);
         }
 
         private void updateStateTransforms(DrawableHitObject hitObject, ArmedState state)
@@ -102,6 +103,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
                     }
 
                     break;
+
+                case ArmedState.Hit:
+                    double animDuration = Math.Min(300, drawableRepeat.HitObject.SpanDuration);
+                    InternalChild.ScaleTo(1.4f, animDuration, Easing.Out);
+                    break;
             }
         }
 
@@ -109,10 +115,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
         {
             base.Dispose(isDisposing);
 
-            if (drawableObject.IsNotNull())
+            if (drawableRepeat.IsNotNull())
             {
-                drawableObject.HitObjectApplied -= onHitObjectApplied;
-                drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
+                drawableRepeat.HitObjectApplied -= onHitObjectApplied;
+                drawableRepeat.ApplyCustomUpdateState -= updateStateTransforms;
             }
         }
     }