mirror of https://github.com/ppy/osu
149 lines
5.2 KiB
C#
149 lines
5.2 KiB
C#
// 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.Audio.Track;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Animations;
|
|
using osu.Framework.Graphics.Textures;
|
|
using osu.Game.Beatmaps.ControlPoints;
|
|
using osu.Game.Graphics.Containers;
|
|
using osu.Game.Skinning;
|
|
using osuTK;
|
|
|
|
namespace osu.Game.Rulesets.Taiko.UI
|
|
{
|
|
public sealed partial class TaikoMascotAnimation : BeatSyncedContainer
|
|
{
|
|
private readonly TextureAnimation textureAnimation;
|
|
|
|
private int currentFrame;
|
|
|
|
public double DisplayTime;
|
|
|
|
public TaikoMascotAnimation(TaikoMascotAnimationState state)
|
|
{
|
|
InternalChild = textureAnimation = createTextureAnimation(state).With(animation =>
|
|
{
|
|
animation.Origin = animation.Anchor = Anchor.BottomLeft;
|
|
animation.Scale = new Vector2(0.51f); // close enough to stable
|
|
});
|
|
|
|
RelativeSizeAxes = Axes.Both;
|
|
Origin = Anchor = Anchor.BottomLeft;
|
|
|
|
// needs to be always present to prevent the animation clock consuming time spent when not present.
|
|
AlwaysPresent = true;
|
|
}
|
|
|
|
public bool Completed => !textureAnimation.IsPlaying || textureAnimation.PlaybackPosition >= textureAnimation.Duration;
|
|
|
|
public override void Show()
|
|
{
|
|
base.Show();
|
|
DisplayTime = Time.Current;
|
|
textureAnimation.Seek(0);
|
|
}
|
|
|
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
|
{
|
|
// assume that if the animation is playing on its own, it's independent from the beat and doesn't need to be touched.
|
|
if (textureAnimation.FrameCount == 0 || textureAnimation.IsPlaying)
|
|
return;
|
|
|
|
textureAnimation.GotoFrame(currentFrame);
|
|
currentFrame = (currentFrame + 1) % textureAnimation.FrameCount;
|
|
}
|
|
|
|
private static TextureAnimation createTextureAnimation(TaikoMascotAnimationState state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case TaikoMascotAnimationState.Clear:
|
|
return new ClearMascotTextureAnimation();
|
|
|
|
case TaikoMascotAnimationState.Idle:
|
|
case TaikoMascotAnimationState.Kiai:
|
|
case TaikoMascotAnimationState.Fail:
|
|
return new ManualMascotTextureAnimation(state);
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(state), $"Mascot animations for state {state} are not supported");
|
|
}
|
|
}
|
|
|
|
private partial class ManualMascotTextureAnimation : TextureAnimation
|
|
{
|
|
private readonly TaikoMascotAnimationState state;
|
|
|
|
public ManualMascotTextureAnimation(TaikoMascotAnimationState state)
|
|
{
|
|
this.state = state;
|
|
|
|
IsPlaying = false;
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load(ISkinSource source)
|
|
{
|
|
ISkin? skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null);
|
|
|
|
if (skin == null) return;
|
|
|
|
for (int frameIndex = 0; true; frameIndex++)
|
|
{
|
|
var texture = getAnimationFrame(skin, state, frameIndex);
|
|
|
|
if (texture == null)
|
|
break;
|
|
|
|
AddFrame(texture);
|
|
}
|
|
}
|
|
}
|
|
|
|
private partial class ClearMascotTextureAnimation : TextureAnimation
|
|
{
|
|
private const float clear_animation_speed = 1000 / 10f;
|
|
|
|
private static readonly int[] clear_animation_sequence = { 0, 1, 2, 3, 4, 5, 6, 5, 6, 5, 4, 3, 2, 1, 0 };
|
|
|
|
public ClearMascotTextureAnimation()
|
|
{
|
|
DefaultFrameLength = clear_animation_speed;
|
|
Loop = false;
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load(ISkinSource source)
|
|
{
|
|
ISkin? skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null);
|
|
|
|
if (skin == null) return;
|
|
|
|
foreach (int frameIndex in clear_animation_sequence)
|
|
{
|
|
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
|
|
|
if (texture == null)
|
|
// as per https://osu.ppy.sh/help/wiki/Skinning/osu!taiko#pippidon
|
|
break;
|
|
|
|
AddFrame(texture);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Texture? getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex)
|
|
{
|
|
var texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}{frameIndex}");
|
|
|
|
if (frameIndex == 0 && texture == null)
|
|
texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}");
|
|
|
|
return texture;
|
|
}
|
|
}
|
|
}
|