2020-11-05 17:00:26 +08:00
// 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.
2020-11-05 17:12:13 +08:00
using System ;
2020-11-05 17:00:26 +08:00
using osu.Framework.Allocation ;
2020-11-05 18:35:32 +08:00
using osu.Framework.Bindables ;
2020-11-05 17:00:26 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2020-11-05 17:12:13 +08:00
using osu.Framework.Graphics.Sprites ;
2020-11-05 17:00:26 +08:00
using osu.Game.Rulesets.Objects.Drawables ;
using osu.Game.Rulesets.Osu.Objects.Drawables ;
2021-03-11 10:40:18 +08:00
using osu.Game.Rulesets.Osu.UI ;
2020-11-05 17:12:13 +08:00
using osu.Game.Skinning ;
2020-11-05 18:05:59 +08:00
using osuTK ;
2020-11-05 17:00:26 +08:00
2020-12-04 19:21:53 +08:00
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
2020-11-05 17:00:26 +08:00
{
public abstract class LegacySpinner : CompositeDrawable
{
2021-03-11 10:40:18 +08:00
/// <remarks>
2021-03-11 19:57:00 +08:00
/// All constant spinner coordinates are in osu!stable's gamefield space, which is shifted 16px downwards.
/// This offset is negated in both osu!stable and osu!lazer to bring all constant coordinates into window-space.
/// Note: SPINNER_Y_CENTRE + SPINNER_TOP_OFFSET - Position.Y = 240 (=480/2, or half the window-space in osu!stable)
2021-03-11 10:40:18 +08:00
/// </remarks>
2021-03-11 19:57:00 +08:00
protected const float SPINNER_TOP_OFFSET = 45f - 16f ;
2021-03-11 10:40:18 +08:00
2021-03-11 19:57:00 +08:00
protected const float SPINNER_Y_CENTRE = SPINNER_TOP_OFFSET + 219f ;
2021-03-08 01:47:16 +08:00
2020-11-05 18:05:59 +08:00
protected const float SPRITE_SCALE = 0.625f ;
2020-11-05 17:00:26 +08:00
protected DrawableSpinner DrawableSpinner { get ; private set ; }
2020-11-05 17:12:13 +08:00
private Sprite spin ;
2020-11-05 18:35:32 +08:00
private Sprite clear ;
2020-11-05 17:12:13 +08:00
2020-11-05 17:00:26 +08:00
[BackgroundDependencyLoader]
2020-11-05 17:12:13 +08:00
private void load ( DrawableHitObject drawableHitObject , ISkinSource source )
2020-11-05 17:00:26 +08:00
{
2021-03-09 13:55:32 +08:00
// legacy spinners relied heavily on absolute screen-space coordinate values.
// wrap everything in a container simulating absolute coords to preserve alignment
// as there are skins that depend on it.
Anchor = Anchor . Centre ;
Origin = Anchor . Centre ;
Size = new Vector2 ( 640 , 480 ) ;
2021-03-11 19:57:00 +08:00
// osu!stable positions components of the spinner in window-space (as opposed to gamefield-space).
// in lazer, the gamefield-space transformation is applied in OsuPlayfieldAdjustmentContainer, which is inverted here to bring coordinates back into window-space.
2021-03-09 13:55:32 +08:00
Position = new Vector2 ( 0 , - 8f ) ;
2020-11-05 17:00:26 +08:00
DrawableSpinner = ( DrawableSpinner ) drawableHitObject ;
2020-11-05 17:12:13 +08:00
2021-03-09 13:55:32 +08:00
AddRangeInternal ( new [ ]
2020-11-05 17:12:13 +08:00
{
2021-03-09 13:55:32 +08:00
spin = new Sprite
2020-11-05 17:12:13 +08:00
{
2021-03-09 13:55:32 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . Centre ,
Depth = float . MinValue ,
Texture = source . GetTexture ( "spinner-spin" ) ,
Scale = new Vector2 ( SPRITE_SCALE ) ,
Y = SPINNER_TOP_OFFSET + 335 ,
} ,
clear = new Sprite
{
Alpha = 0 ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . Centre ,
Depth = float . MinValue ,
Texture = source . GetTexture ( "spinner-clear" ) ,
Scale = new Vector2 ( SPRITE_SCALE ) ,
Y = SPINNER_TOP_OFFSET + 115 ,
} ,
2020-11-05 17:12:13 +08:00
} ) ;
2020-11-05 17:00:26 +08:00
}
2020-11-05 18:35:32 +08:00
private readonly Bindable < bool > completed = new Bindable < bool > ( ) ;
2020-11-05 17:00:26 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2020-11-05 18:35:32 +08:00
completed . BindValueChanged ( onCompletedChanged , true ) ;
2020-11-05 17:00:26 +08:00
DrawableSpinner . ApplyCustomUpdateState + = UpdateStateTransforms ;
UpdateStateTransforms ( DrawableSpinner , DrawableSpinner . State . Value ) ;
}
2020-11-05 18:35:32 +08:00
private void onCompletedChanged ( ValueChangedEvent < bool > completed )
{
if ( completed . NewValue )
{
2020-11-05 18:44:34 +08:00
double startTime = Math . Min ( Time . Current , DrawableSpinner . HitStateUpdateTime - 400 ) ;
using ( BeginAbsoluteSequence ( startTime , true ) )
{
clear . FadeInFromZero ( 400 , Easing . Out ) ;
clear . ScaleTo ( SPRITE_SCALE * 2 )
. Then ( ) . ScaleTo ( SPRITE_SCALE * 0.8f , 240 , Easing . Out )
. Then ( ) . ScaleTo ( SPRITE_SCALE , 160 ) ;
}
2020-11-05 18:35:32 +08:00
const double fade_out_duration = 50 ;
using ( BeginAbsoluteSequence ( DrawableSpinner . HitStateUpdateTime - fade_out_duration , true ) )
clear . FadeOut ( fade_out_duration ) ;
}
else
{
clear . ClearTransforms ( ) ;
clear . Alpha = 0 ;
}
}
2020-11-15 05:42:19 +08:00
protected override void Update ( )
{
base . Update ( ) ;
completed . Value = Time . Current > = DrawableSpinner . Result . TimeCompleted ;
}
2020-11-05 17:00:26 +08:00
protected virtual void UpdateStateTransforms ( DrawableHitObject drawableHitObject , ArmedState state )
{
2020-11-05 17:12:13 +08:00
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 ;
}
2020-11-05 17:00:26 +08:00
}
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
if ( DrawableSpinner ! = null )
DrawableSpinner . ApplyCustomUpdateState - = UpdateStateTransforms ;
}
}
}