diff --git a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs index b5ec2e8def..19493271be 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs @@ -1,21 +1,64 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.Skinning { public class LegacyHitExplosion : CompositeDrawable { - public LegacyHitExplosion(Drawable sprite) - { - InternalChild = sprite; + private readonly Drawable sprite; + private readonly Drawable strongSprite; + private DrawableStrongNestedHit nestedStrongHit; + private bool switchedToStrongSprite; + + /// + /// Creates a new legacy hit explosion. + /// + /// + /// Contrary to stable's, this implementation doesn't require a frame-perfect hit + /// for the strong sprite to be displayed. + /// + /// The normal legacy explosion sprite. + /// The strong legacy explosion sprite. + public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null) + { + this.sprite = sprite; + this.strongSprite = strongSprite; + } + + [BackgroundDependencyLoader] + private void load(DrawableHitObject judgedObject) + { Anchor = Anchor.Centre; Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; + + AddInternal(sprite.With(s => + { + s.Anchor = Anchor.Centre; + s.Origin = Anchor.Centre; + })); + + if (strongSprite != null) + { + AddInternal(strongSprite.With(s => + { + s.Alpha = 0; + s.Anchor = Anchor.Centre; + s.Origin = Anchor.Centre; + })); + } + + if (judgedObject is DrawableHit hit) + nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit; } protected override void LoadComplete() @@ -33,5 +76,25 @@ namespace osu.Game.Rulesets.Taiko.Skinning Expire(true); } + + protected override void Update() + { + base.Update(); + + if (shouldSwitchToStrongSprite() && !switchedToStrongSprite) + { + sprite.FadeOut(50, Easing.OutQuint); + strongSprite.FadeIn(50, Easing.OutQuint); + switchedToStrongSprite = true; + } + } + + private bool shouldSwitchToStrongSprite() + { + if (nestedStrongHit == null || strongSprite == null) + return false; + + return nestedStrongHit.IsHit; + } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index c222ccb51f..d320b824e6 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -74,15 +74,23 @@ namespace osu.Game.Rulesets.Taiko.Skinning return null; - case TaikoSkinComponents.TaikoExplosionGood: - case TaikoSkinComponents.TaikoExplosionGoodStrong: - case TaikoSkinComponents.TaikoExplosionGreat: - case TaikoSkinComponents.TaikoExplosionGreatStrong: case TaikoSkinComponents.TaikoExplosionMiss: - var sprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); - if (sprite != null) - return new LegacyHitExplosion(sprite); + var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); + if (missSprite != null) + return new LegacyHitExplosion(missSprite); + + return null; + + case TaikoSkinComponents.TaikoExplosionGood: + case TaikoSkinComponents.TaikoExplosionGreat: + + var hitName = getHitName(taikoComponent.Component); + var hitSprite = this.GetAnimation(hitName, true, false); + var strongHitSprite = this.GetAnimation($"{hitName}k", true, false); + + if (hitSprite != null) + return new LegacyHitExplosion(hitSprite, strongHitSprite); return null; @@ -109,17 +117,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.TaikoExplosionGood: return "taiko-hit100"; - case TaikoSkinComponents.TaikoExplosionGoodStrong: - return "taiko-hit100k"; - case TaikoSkinComponents.TaikoExplosionGreat: return "taiko-hit300"; - - case TaikoSkinComponents.TaikoExplosionGreatStrong: - return "taiko-hit300k"; } - throw new ArgumentOutOfRangeException(nameof(component), "Invalid result type"); + throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}"); } public override SampleChannel GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 0d785adb4a..ac4fb51661 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -17,9 +17,7 @@ namespace osu.Game.Rulesets.Taiko BarLine, TaikoExplosionMiss, TaikoExplosionGood, - TaikoExplosionGoodStrong, TaikoExplosionGreat, - TaikoExplosionGreatStrong, Scroller, Mascot, } diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index efd1b25046..53765f04dd 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -10,7 +9,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.UI @@ -50,10 +48,10 @@ namespace osu.Game.Rulesets.Taiko.UI [BackgroundDependencyLoader] private void load() { - Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject)), _ => new DefaultHitExplosion(JudgedObject, result)); + Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result)); } - private TaikoSkinComponents getComponentName(DrawableHitObject judgedObject) + private static TaikoSkinComponents getComponentName(HitResult result) { switch (result) { @@ -61,28 +59,13 @@ namespace osu.Game.Rulesets.Taiko.UI return TaikoSkinComponents.TaikoExplosionMiss; case HitResult.Good: - return useStrongExplosion(judgedObject) - ? TaikoSkinComponents.TaikoExplosionGoodStrong - : TaikoSkinComponents.TaikoExplosionGood; + return TaikoSkinComponents.TaikoExplosionGood; case HitResult.Great: - return useStrongExplosion(judgedObject) - ? TaikoSkinComponents.TaikoExplosionGreatStrong - : TaikoSkinComponents.TaikoExplosionGreat; + return TaikoSkinComponents.TaikoExplosionGreat; } - throw new ArgumentOutOfRangeException(nameof(judgedObject), "Invalid result type"); - } - - private bool useStrongExplosion(DrawableHitObject judgedObject) - { - if (!(judgedObject.HitObject is Hit)) - return false; - - if (!(judgedObject.NestedHitObjects.SingleOrDefault() is DrawableStrongNestedHit nestedHit)) - return false; - - return judgedObject.Result.Type == nestedHit.Result.Type; + throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}"); } /// diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 7b3fbb1faf..120cf264c3 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -215,16 +215,12 @@ namespace osu.Game.Rulesets.Taiko.UI private void addDrumRollHit(DrawableDrumRollTick drawableTick) => drumRollHitContainer.Add(new DrawableFlyingHit(drawableTick)); - /// - /// As legacy skins have different explosions for singular and double strong hits, - /// explosion addition is scheduled to ensure that both hits are processed if they occur on the same frame. - /// - private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type) => Schedule(() => + private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type) { hitExplosionContainer.Add(new HitExplosion(drawableObject, result)); if (drawableObject.HitObject.Kiai) kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); - }); + } private class ProxyContainer : LifetimeManagementContainer {