diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
index 31e2ab1239..05f4c8e307 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
/// Legacy skinned spinner with two main spinning layers, one fixed overlay and one final spinning overlay.
/// No background layer.
///
- public class LegacyNewStyleSpinner : CompositeDrawable
+ public class LegacyNewStyleSpinner : LegacySpinner
{
private Sprite glow;
private Sprite discBottom;
@@ -27,66 +27,61 @@ namespace osu.Game.Rulesets.Osu.Skinning
private Sprite spinningMiddle;
private Sprite fixedMiddle;
- private DrawableSpinner drawableSpinner;
-
- private const float final_scale = 0.625f;
-
private readonly Color4 glowColour = new Color4(3, 151, 255, 255);
+ private Container scaleContainer;
+
[BackgroundDependencyLoader]
- private void load(ISkinSource source, DrawableHitObject drawableObject)
+ private void load(ISkinSource source)
{
- drawableSpinner = (DrawableSpinner)drawableObject;
-
- Scale = new Vector2(final_scale);
-
- InternalChildren = new Drawable[]
+ AddInternal(scaleContainer = new Container
{
- glow = new Sprite
+ Scale = new Vector2(SPRITE_SCALE),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-glow"),
- Blending = BlendingParameters.Additive,
- Colour = glowColour,
- },
- discBottom = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-bottom")
- },
- discTop = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-top")
- },
- fixedMiddle = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-middle")
- },
- spinningMiddle = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-middle2")
+ glow = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-glow"),
+ Blending = BlendingParameters.Additive,
+ Colour = glowColour,
+ },
+ discBottom = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-bottom")
+ },
+ discTop = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-top")
+ },
+ fixedMiddle = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-middle")
+ },
+ spinningMiddle = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-middle2")
+ }
}
- };
+ });
}
- protected override void LoadComplete()
+ protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
- base.LoadComplete();
+ base.UpdateStateTransforms(drawableHitObject, state);
- drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
- updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
- }
-
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
- {
switch (drawableHitObject)
{
case DrawableSpinner d:
@@ -125,20 +120,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
protected override void Update()
{
base.Update();
- spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation;
+ spinningMiddle.Rotation = discTop.Rotation = DrawableSpinner.RotationTracker.Rotation;
discBottom.Rotation = discTop.Rotation / 3;
- glow.Alpha = drawableSpinner.Progress;
+ glow.Alpha = DrawableSpinner.Progress;
- Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f));
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (drawableSpinner != null)
- drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
+ scaleContainer.Scale = new Vector2(SPRITE_SCALE * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f));
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
index 1954ff6e38..fba802f085 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
@@ -18,28 +18,22 @@ namespace osu.Game.Rulesets.Osu.Skinning
///
/// Legacy skinned spinner with one main spinning layer and a background layer.
///
- public class LegacyOldStyleSpinner : CompositeDrawable
+ public class LegacyOldStyleSpinner : LegacySpinner
{
- private DrawableSpinner drawableSpinner;
private Sprite disc;
private Sprite metreSprite;
private Container metre;
private bool spinnerBlink;
- private const float sprite_scale = 1 / 1.6f;
- private const float final_metre_height = 692 * sprite_scale;
+ private const float final_metre_height = 692 * SPRITE_SCALE;
[BackgroundDependencyLoader]
- private void load(ISkinSource source, DrawableHitObject drawableObject)
+ private void load(ISkinSource source)
{
spinnerBlink = source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true;
- drawableSpinner = (DrawableSpinner)drawableObject;
-
- RelativeSizeAxes = Axes.Both;
-
- InternalChild = new Container
+ AddInternal(new Container
{
// the old-style spinner relied heavily on absolute screen-space coordinate values.
// wrap everything in a container simulating absolute coords to preserve alignment
@@ -55,14 +49,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-background"),
- Scale = new Vector2(sprite_scale)
+ Scale = new Vector2(SPRITE_SCALE)
},
disc = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-circle"),
- Scale = new Vector2(sprite_scale)
+ Scale = new Vector2(SPRITE_SCALE)
},
metre = new Container
{
@@ -78,23 +72,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
Texture = source.GetTexture("spinner-metre"),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
- Scale = new Vector2(0.625f)
+ Scale = new Vector2(SPRITE_SCALE)
}
}
}
- };
+ });
}
- protected override void LoadComplete()
+ protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
- base.LoadComplete();
+ base.UpdateStateTransforms(drawableHitObject, state);
- drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
- updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
- }
-
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
- {
if (!(drawableHitObject is DrawableSpinner d))
return;
@@ -110,11 +98,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
protected override void Update()
{
base.Update();
- disc.Rotation = drawableSpinner.RotationTracker.Rotation;
+ disc.Rotation = DrawableSpinner.RotationTracker.Rotation;
// careful: need to call this exactly once for all calculations in a frame
// as the function has a random factor in it
- var metreHeight = getMetreHeight(drawableSpinner.Progress);
+ var metreHeight = getMetreHeight(DrawableSpinner.Progress);
// hack to make the metre blink up from below than down from above.
// move down the container to be able to apply masking for the metre,
@@ -140,13 +128,5 @@ namespace osu.Game.Rulesets.Osu.Skinning
return (float)barCount / total_bars * final_metre_height;
}
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (drawableSpinner != null)
- drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
new file mode 100644
index 0000000000..31350791e3
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
@@ -0,0 +1,84 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Skinning
+{
+ public abstract class LegacySpinner : CompositeDrawable
+ {
+ protected const float SPRITE_SCALE = 0.625f;
+
+ protected DrawableSpinner DrawableSpinner { get; private set; }
+
+ private Sprite spin;
+
+ [BackgroundDependencyLoader]
+ private void load(DrawableHitObject drawableHitObject, ISkinSource source)
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ DrawableSpinner = (DrawableSpinner)drawableHitObject;
+
+ AddRangeInternal(new[]
+ {
+ spin = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Depth = float.MinValue,
+ Texture = source.GetTexture("spinner-spin"),
+ Scale = new Vector2(SPRITE_SCALE),
+ Y = 120 - 45 // offset temporarily to avoid overlapping default spin counter
+ },
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms;
+ UpdateStateTransforms(DrawableSpinner, DrawableSpinner.State.Value);
+ }
+
+ protected virtual void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
+ {
+ switch (drawableHitObject)
+ {
+ case DrawableSpinner d:
+ double fadeOutLength = Math.Min(400, d.HitObject.Duration);
+
+ using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - fadeOutLength, true))
+ spin.FadeOutFromOne(fadeOutLength);
+
+ break;
+
+ case DrawableSpinnerTick d:
+ if (state == ArmedState.Hit)
+ {
+ using (BeginAbsoluteSequence(d.HitStateUpdateTime, true))
+ spin.FadeOut(300);
+ }
+
+ break;
+ }
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (DrawableSpinner != null)
+ DrawableSpinner.ApplyCustomUpdateState -= UpdateStateTransforms;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index c7e2e0638c..5c3f57c2d0 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -546,7 +546,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = HitObject.GetEndTime();
- Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
+ Result.TimeOffset = Time.Current - endTime;
+
+ double missWindow = HitObject.HitWindows.WindowFor(HitResult.Miss);
+ if (missWindow > 0)
+ Result.TimeOffset = Math.Min(Result.TimeOffset, missWindow);
if (Result.HasResult)
updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);