// 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.Shapes; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Taiko.Skinning.Argon { public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement { private RingExplosion? ringExplosion; [Resolved] private OsuColour colours { get; set; } = null!; public ArgonJudgementPiece(HitResult result) : base(result) { RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load() { InternalChildren = new Drawable[] { JudgementText = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = colours.ForHitResult(Result), Blending = BlendingParameters.Additive, Spacing = new Vector2(10, 0), RelativePositionAxes = Axes.Both, Font = OsuFont.Default.With(size: 20, weight: FontWeight.Regular), }, }; if (Result.IsHit()) { AddInternal(ringExplosion = new RingExplosion(Result) { Colour = colours.ForHitResult(Result), RelativePositionAxes = Axes.Y, }); } } /// /// Plays the default animation for this judgement piece. /// /// /// The base implementation only handles fade (for all result types) and misses. /// Individual rulesets are recommended to implement their appropriate hit animations. /// public virtual void PlayAnimation() { const double duration = 800; switch (Result) { default: JudgementText.MoveToY(-0.6f) .MoveToY(-1.0f, duration, Easing.OutQuint); JudgementText .ScaleTo(Vector2.One) .ScaleTo(new Vector2(1.4f), duration, Easing.OutQuint); break; case HitResult.Miss: this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); JudgementText.MoveTo(Vector2.Zero); JudgementText.MoveToOffset(new Vector2(0, 100), duration, Easing.InQuint); this.RotateTo(0); this.RotateTo(40, duration, Easing.InQuint); break; } this.FadeOutFromOne(duration, Easing.OutQuint); ringExplosion?.PlayAnimation(); } public Drawable? GetAboveHitObjectsProxiedContent() => null; private partial class RingExplosion : CompositeDrawable { private readonly float travel = 58; public RingExplosion(HitResult result) { const float thickness = 4; const float small_size = 9; const float large_size = 14; Anchor = Anchor.Centre; Origin = Anchor.Centre; Blending = BlendingParameters.Additive; int countSmall = 0; int countLarge = 0; switch (result) { case HitResult.Meh: countSmall = 3; travel *= 0.3f; break; case HitResult.Ok: case HitResult.Good: countSmall = 4; travel *= 0.6f; break; case HitResult.Great: case HitResult.Perfect: countSmall = 4; countLarge = 4; break; } for (int i = 0; i < countSmall; i++) AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) }); for (int i = 0; i < countLarge; i++) AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) }); } public void PlayAnimation() { foreach (var c in InternalChildren) { const float start_position_ratio = 0.6f; float direction = RNG.NextSingle(0, 360); float distance = RNG.NextSingle(travel / 2, travel); c.MoveTo(new Vector2( MathF.Cos(direction) * distance * start_position_ratio, MathF.Sin(direction) * distance * start_position_ratio )); c.MoveTo(new Vector2( MathF.Cos(direction) * distance, MathF.Sin(direction) * distance ), 600, Easing.OutQuint); } this.FadeOutFromOne(1000, Easing.OutQuint); } public partial class RingPiece : CircularContainer { public RingPiece(float thickness = 9) { Anchor = Anchor.Centre; Origin = Anchor.Centre; Masking = true; BorderThickness = thickness; BorderColour = Color4.White; Child = new Box { AlwaysPresent = true, Alpha = 0, RelativeSizeAxes = Axes.Both }; } } } } }