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);