2019-01-24 16:43:03 +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.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2018-04-13 17:19:50 +08:00
using System ;
using osu.Framework.Allocation ;
using osu.Framework.Audio ;
using osu.Framework.Audio.Sample ;
using osu.Framework.Audio.Track ;
2020-03-11 09:18:41 +08:00
using osu.Framework.Extensions.Color4Extensions ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Graphics.Sprites ;
using osu.Framework.Graphics.Textures ;
2018-10-02 11:02:47 +08:00
using osu.Framework.Input.Events ;
2020-01-09 12:43:44 +08:00
using osu.Framework.Utils ;
2018-04-13 17:19:50 +08:00
using osu.Game.Beatmaps.ControlPoints ;
using osu.Game.Graphics.Backgrounds ;
using osu.Game.Graphics.Containers ;
2020-08-04 20:53:00 +08:00
using osu.Game.Overlays ;
2018-11-20 15:51:59 +08:00
using osuTK ;
using osuTK.Graphics ;
using osuTK.Input ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Screens.Menu
{
/// <summary>
/// osu! logo and its attachments (pulsing, visualiser etc.)
/// </summary>
public class OsuLogo : BeatSyncedContainer
{
2020-03-11 09:18:41 +08:00
public readonly Color4 OsuPink = Color4Extensions . FromHex ( @"e967a1" ) ;
2018-04-13 17:19:50 +08:00
private const double transition_length = 300 ;
private readonly Sprite logo ;
private readonly CircularContainer logoContainer ;
private readonly Container logoBounceContainer ;
private readonly Container logoBeatContainer ;
private readonly Container logoAmplitudeContainer ;
private readonly Container logoHoverContainer ;
2020-06-09 05:45:40 +08:00
private readonly MenuLogoVisualisation visualizer ;
2018-04-13 17:19:50 +08:00
private readonly IntroSequence intro ;
2021-01-19 16:11:40 +08:00
private Sample sampleClick ;
private Sample sampleBeat ;
2021-02-12 16:22:15 +08:00
private Sample sampleDownbeat ;
2018-04-13 17:19:50 +08:00
private readonly Container colourAndTriangles ;
private readonly Triangles triangles ;
/// <summary>
/// Return value decides whether the logo should play its own sample for the click action.
/// </summary>
public Func < bool > Action ;
2019-04-05 11:02:47 +08:00
/// <summary>
/// The size of the logo Sprite with respect to the scale of its hover and bounce containers.
/// </summary>
/// <remarks>Does not account for the scale of this <see cref="OsuLogo"/></remarks>
2019-03-28 14:40:58 +08:00
public float SizeForFlow = > logo = = null ? 0 : logo . DrawSize . X * logo . Scale . X * logoBounceContainer . Scale . X * logoHoverContainer . Scale . X ;
2018-04-13 17:19:50 +08:00
2019-04-05 12:56:08 +08:00
public bool IsTracking { get ; set ; }
2019-04-05 11:02:47 +08:00
2018-04-13 17:19:50 +08:00
private readonly Sprite ripple ;
private readonly Container rippleContainer ;
public bool Triangles
{
2019-02-28 12:58:19 +08:00
set = > colourAndTriangles . FadeTo ( value ? 1 : 0 , transition_length , Easing . OutQuint ) ;
2018-04-13 17:19:50 +08:00
}
2018-09-26 13:01:15 +08:00
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos ) = > logoContainer . ReceivePositionalInputAt ( screenSpacePos ) ;
2018-04-13 17:19:50 +08:00
public bool Ripple
{
2019-02-28 12:58:19 +08:00
get = > rippleContainer . Alpha > 0 ;
set = > rippleContainer . FadeTo ( value ? 1 : 0 , transition_length , Easing . OutQuint ) ;
2018-04-13 17:19:50 +08:00
}
2020-11-23 13:27:02 +08:00
private const float visualizer_default_alpha = 0.5f ;
2018-04-13 17:19:50 +08:00
private readonly Box flashLayer ;
private readonly Container impactContainer ;
private const double early_activation = 60 ;
2018-09-08 15:31:00 +08:00
public override bool IsPresent = > base . IsPresent | | Scheduler . HasPendingTasks ;
2018-04-13 17:19:50 +08:00
public OsuLogo ( )
{
EarlyActivationMilliseconds = early_activation ;
Origin = Anchor . Centre ;
AutoSizeAxes = Axes . Both ;
Children = new Drawable [ ]
{
intro = new IntroSequence
{
RelativeSizeAxes = Axes . Both ,
} ,
logoHoverContainer = new Container
{
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
2022-04-28 21:58:32 +08:00
logoBounceContainer = new DragContainer
2018-04-13 17:19:50 +08:00
{
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
rippleContainer = new Container
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
ripple = new Sprite
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2019-08-21 12:29:50 +08:00
Blending = BlendingParameters . Additive ,
2018-04-13 17:19:50 +08:00
Alpha = 0
}
}
} ,
logoAmplitudeContainer = new Container
{
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
logoBeatContainer = new Container
{
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
2020-06-09 05:45:40 +08:00
visualizer = new MenuLogoVisualisation
2018-04-13 17:19:50 +08:00
{
RelativeSizeAxes = Axes . Both ,
Origin = Anchor . Centre ,
Anchor = Anchor . Centre ,
2020-11-23 13:27:02 +08:00
Alpha = visualizer_default_alpha ,
2018-04-13 17:19:50 +08:00
Size = new Vector2 ( 0.96f )
} ,
new Container
{
AutoSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
logoContainer = new CircularContainer
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
RelativeSizeAxes = Axes . Both ,
Scale = new Vector2 ( 0.88f ) ,
Masking = true ,
Children = new Drawable [ ]
{
colourAndTriangles = new Container
{
RelativeSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
Colour = OsuPink ,
} ,
triangles = new Triangles
{
TriangleScale = 4 ,
2020-03-11 09:18:41 +08:00
ColourLight = Color4Extensions . FromHex ( @"ff7db7" ) ,
ColourDark = Color4Extensions . FromHex ( @"de5b95" ) ,
2018-04-13 17:19:50 +08:00
RelativeSizeAxes = Axes . Both ,
} ,
}
} ,
flashLayer = new Box
{
RelativeSizeAxes = Axes . Both ,
2019-08-21 12:29:50 +08:00
Blending = BlendingParameters . Additive ,
2018-04-13 17:19:50 +08:00
Colour = Color4 . White ,
Alpha = 0 ,
} ,
} ,
} ,
logo = new Sprite
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ,
}
} ,
impactContainer = new CircularContainer
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Alpha = 0 ,
BorderColour = Color4 . White ,
RelativeSizeAxes = Axes . Both ,
BorderThickness = 10 ,
Masking = true ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
AlwaysPresent = true ,
Alpha = 0 ,
}
}
}
}
}
}
}
}
}
}
}
} ;
}
/// <summary>
2019-08-31 01:48:45 +08:00
/// Schedule a new external animation. Handled queueing and finishing previous animations in a sane way.
2018-04-13 17:19:50 +08:00
/// </summary>
/// <param name="action">The animation to be performed</param>
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>
public void AppendAnimatingAction ( Action action , bool waitForPrevious )
{
void runnableAction ( )
{
if ( waitForPrevious )
this . DelayUntilTransformsFinished ( ) . Schedule ( action ) ;
else
{
ClearTransforms ( ) ;
action ( ) ;
}
}
if ( IsLoaded )
runnableAction ( ) ;
else
Schedule ( runnableAction ) ;
}
[BackgroundDependencyLoader]
2018-08-31 06:04:40 +08:00
private void load ( TextureStore textures , AudioManager audio )
2018-04-13 17:19:50 +08:00
{
2019-05-28 16:06:01 +08:00
sampleClick = audio . Samples . Get ( @"Menu/osu-logo-select" ) ;
sampleBeat = audio . Samples . Get ( @"Menu/osu-logo-heartbeat" ) ;
2021-02-10 17:14:32 +08:00
sampleDownbeat = audio . Samples . Get ( @"Menu/osu-logo-downbeat" ) ;
2018-04-13 17:19:50 +08:00
2018-08-31 06:04:40 +08:00
logo . Texture = textures . Get ( @"Menu/logo" ) ;
ripple . Texture = textures . Get ( @"Menu/logo" ) ;
2018-04-13 17:19:50 +08:00
}
private int lastBeatIndex ;
2020-06-23 12:49:18 +08:00
protected override void OnNewBeat ( int beatIndex , TimingControlPoint timingPoint , EffectControlPoint effectPoint , ChannelAmplitudes amplitudes )
2018-04-13 17:19:50 +08:00
{
base . OnNewBeat ( beatIndex , timingPoint , effectPoint , amplitudes ) ;
lastBeatIndex = beatIndex ;
2021-10-27 12:04:41 +08:00
double beatLength = timingPoint . BeatLength ;
2018-04-13 17:19:50 +08:00
float amplitudeAdjust = Math . Min ( 1 , 0.4f + amplitudes . Maximum ) ;
if ( beatIndex < 0 ) return ;
if ( IsHovered )
2021-02-10 17:14:32 +08:00
{
this . Delay ( early_activation ) . Schedule ( ( ) = >
{
2022-01-23 00:27:27 +08:00
if ( beatIndex % timingPoint . TimeSignature . Numerator = = 0 )
2022-04-15 19:32:41 +08:00
{
sampleDownbeat ? . Play ( ) ;
}
2021-02-10 17:14:32 +08:00
else
2022-04-15 19:32:41 +08:00
{
var channel = sampleBeat . GetChannel ( ) ;
channel . Frequency . Value = 0.95 + RNG . NextDouble ( 0.1 ) ;
channel . Play ( ) ;
}
2021-02-10 17:14:32 +08:00
} ) ;
}
2018-04-13 17:19:50 +08:00
logoBeatContainer
2020-11-23 13:27:02 +08:00
. ScaleTo ( 1 - 0.02f * amplitudeAdjust , early_activation , Easing . Out ) . Then ( )
2018-04-13 17:19:50 +08:00
. ScaleTo ( 1 , beatLength * 2 , Easing . OutQuint ) ;
ripple . ClearTransforms ( ) ;
ripple
. ScaleTo ( logoAmplitudeContainer . Scale )
. ScaleTo ( logoAmplitudeContainer . Scale * ( 1 + 0.04f * amplitudeAdjust ) , beatLength , Easing . OutQuint )
. FadeTo ( 0.15f * amplitudeAdjust ) . FadeOut ( beatLength , Easing . OutQuint ) ;
if ( effectPoint . KiaiMode & & flashLayer . Alpha < 0.4f )
{
flashLayer . ClearTransforms ( ) ;
flashLayer
2020-11-23 13:27:02 +08:00
. FadeTo ( 0.2f * amplitudeAdjust , early_activation , Easing . Out ) . Then ( )
2018-04-13 17:19:50 +08:00
. FadeOut ( beatLength ) ;
visualizer . ClearTransforms ( ) ;
visualizer
2020-11-23 13:27:02 +08:00
. FadeTo ( visualizer_default_alpha * 1.8f * amplitudeAdjust , early_activation , Easing . Out ) . Then ( )
. FadeTo ( visualizer_default_alpha , beatLength ) ;
2018-04-13 17:19:50 +08:00
}
}
public void PlayIntro ( )
{
const double length = 3150 ;
const double fade = 200 ;
logoHoverContainer . FadeOut ( ) . Delay ( length ) . FadeIn ( fade ) ;
intro . Show ( ) ;
intro . Start ( length ) ;
intro . Delay ( length + fade ) . FadeOut ( ) ;
}
2020-08-04 20:53:00 +08:00
[Resolved]
private MusicController musicController { get ; set ; }
2018-04-13 17:19:50 +08:00
protected override void Update ( )
{
base . Update ( ) ;
const float scale_adjust_cutoff = 0.4f ;
const float velocity_adjust_cutoff = 0.98f ;
const float paused_velocity = 0.5f ;
2020-08-07 19:51:56 +08:00
if ( musicController . CurrentTrack . IsRunning )
2018-04-13 17:19:50 +08:00
{
2021-10-27 12:04:41 +08:00
float maxAmplitude = lastBeatIndex > = 0 ? musicController . CurrentTrack . CurrentAmplitudes . Maximum : 0 ;
2020-07-27 14:10:32 +08:00
logoAmplitudeContainer . Scale = new Vector2 ( ( float ) Interpolation . Damp ( logoAmplitudeContainer . Scale . X , 1 - Math . Max ( 0 , maxAmplitude - scale_adjust_cutoff ) * 0.04f , 0.9f , Time . Elapsed ) ) ;
2018-04-13 17:19:50 +08:00
if ( maxAmplitude > velocity_adjust_cutoff )
triangles . Velocity = 1 + Math . Max ( 0 , maxAmplitude - velocity_adjust_cutoff ) * 50 ;
else
triangles . Velocity = ( float ) Interpolation . Damp ( triangles . Velocity , 1 , 0.995f , Time . Elapsed ) ;
}
else
{
triangles . Velocity = paused_velocity ;
}
}
2018-09-26 13:01:15 +08:00
public override bool HandlePositionalInput = > base . HandlePositionalInput & & Action ! = null & & Alpha > 0.2f ;
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override bool OnMouseDown ( MouseDownEvent e )
2018-04-13 17:19:50 +08:00
{
2018-10-02 11:02:47 +08:00
if ( e . Button ! = MouseButton . Left ) return false ;
2018-07-12 11:15:22 +08:00
2018-04-13 17:19:50 +08:00
logoBounceContainer . ScaleTo ( 0.9f , 1000 , Easing . Out ) ;
return true ;
}
2020-01-20 17:17:21 +08:00
protected override void OnMouseUp ( MouseUpEvent e )
2018-04-13 17:19:50 +08:00
{
2020-01-20 17:17:21 +08:00
if ( e . Button ! = MouseButton . Left ) return ;
2018-07-12 11:15:22 +08:00
2018-04-13 17:19:50 +08:00
logoBounceContainer . ScaleTo ( 1f , 500 , Easing . OutElastic ) ;
}
2018-10-02 11:02:47 +08:00
protected override bool OnClick ( ClickEvent e )
2018-04-13 17:19:50 +08:00
{
if ( Action ? . Invoke ( ) ? ? true )
sampleClick . Play ( ) ;
flashLayer . ClearTransforms ( ) ;
flashLayer . Alpha = 0.4f ;
flashLayer . FadeOut ( 1500 , Easing . OutExpo ) ;
return true ;
}
2018-10-02 11:02:47 +08:00
protected override bool OnHover ( HoverEvent e )
2018-04-13 17:19:50 +08:00
{
logoHoverContainer . ScaleTo ( 1.1f , 500 , Easing . OutElastic ) ;
return true ;
}
2018-10-02 11:02:47 +08:00
protected override void OnHoverLost ( HoverLostEvent e )
2018-04-13 17:19:50 +08:00
{
logoHoverContainer . ScaleTo ( 1 , 500 , Easing . OutElastic ) ;
}
public void Impact ( )
{
impactContainer . FadeOutFromOne ( 250 , Easing . In ) ;
impactContainer . ScaleTo ( 0.96f ) ;
impactContainer . ScaleTo ( 1.12f , 250 ) ;
}
2022-04-28 21:58:32 +08:00
private class DragContainer : Container
{
2022-04-29 11:09:11 +08:00
public override bool DragBlocksClick = > false ;
2022-04-28 21:58:32 +08:00
protected override bool OnDragStart ( DragStartEvent e ) = > true ;
protected override void OnDrag ( DragEvent e )
{
Vector2 change = e . MousePosition - e . MouseDownPosition ;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change * = change . Length < = 0 ? 0 : MathF . Pow ( change . Length , 0.6f ) / change . Length ;
this . MoveTo ( change ) ;
}
protected override void OnDragEnd ( DragEndEvent e )
{
this . MoveTo ( Vector2 . Zero , 800 , Easing . OutElastic ) ;
base . OnDragEnd ( e ) ;
}
}
2018-04-13 17:19:50 +08:00
}
}