2018-01-05 19:21:19 +08:00
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
2017-02-07 12:59:30 +08:00
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
2016-10-07 16:07:23 +08:00
using System ;
2016-11-14 16:23:33 +08:00
using osu.Framework.Allocation ;
2017-02-07 20:45:59 +08:00
using osu.Framework.Audio ;
using osu.Framework.Audio.Sample ;
2017-05-24 00:17:09 +08:00
using osu.Framework.Audio.Track ;
2016-10-07 16:07:23 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2017-06-20 13:54:23 +08:00
using osu.Framework.Graphics.Shapes ;
2016-10-07 16:07:23 +08:00
using osu.Framework.Graphics.Sprites ;
2016-11-14 16:23:33 +08:00
using osu.Framework.Graphics.Textures ;
2016-10-07 16:07:23 +08:00
using osu.Framework.Input ;
2017-05-24 12:05:11 +08:00
using osu.Framework.MathUtils ;
2017-05-24 00:17:09 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2017-01-27 12:50:22 +08:00
using osu.Game.Graphics ;
2016-12-01 19:21:14 +08:00
using osu.Game.Graphics.Backgrounds ;
2017-05-23 11:29:43 +08:00
using osu.Game.Graphics.Containers ;
2016-10-15 21:31:16 +08:00
using OpenTK ;
2016-12-01 19:21:14 +08:00
using OpenTK.Graphics ;
2016-10-07 16:07:23 +08:00
2016-11-14 16:23:33 +08:00
namespace osu.Game.Screens.Menu
2016-10-07 16:07:23 +08:00
{
/// <summary>
/// osu! logo and its attachments (pulsing, visualiser etc.)
/// </summary>
2017-05-23 11:29:43 +08:00
public class OsuLogo : BeatSyncedContainer
2016-10-07 16:07:23 +08:00
{
2017-05-08 18:56:04 +08:00
public readonly Color4 OsuPink = OsuColour . FromHex ( @"e967a1" ) ;
2017-01-27 16:57:52 +08:00
2017-11-01 19:54:58 +08:00
private const double transition_length = 300 ;
2017-03-23 12:41:50 +08:00
private readonly Sprite logo ;
private readonly CircularContainer logoContainer ;
private readonly Container logoBounceContainer ;
2017-05-23 11:29:43 +08:00
private readonly Container logoBeatContainer ;
2017-05-24 00:45:01 +08:00
private readonly Container logoAmplitudeContainer ;
2017-03-23 12:41:50 +08:00
private readonly Container logoHoverContainer ;
2017-06-19 08:33:50 +08:00
private readonly LogoVisualisation visualizer ;
2016-10-07 16:07:23 +08:00
2017-11-03 16:54:35 +08:00
private readonly IntroSequence intro ;
2017-02-18 16:35:04 +08:00
private SampleChannel sampleClick ;
2017-06-19 22:37:00 +08:00
private SampleChannel sampleBeat ;
2017-02-07 20:45:59 +08:00
2017-03-23 12:41:50 +08:00
private readonly Container colourAndTriangles ;
2016-12-01 19:21:14 +08:00
2017-05-24 12:29:12 +08:00
private readonly Triangles triangles ;
2017-05-24 12:05:11 +08:00
2017-11-25 22:29:08 +08:00
/// <summary>
/// Return value decides whether the logo should play its own sample for the click action.
/// </summary>
public Func < bool > Action ;
2016-10-07 16:07:23 +08:00
2017-05-15 18:36:03 +08:00
public float SizeForFlow = > logo = = null ? 0 : logo . DrawSize . X * logo . Scale . X * logoBounceContainer . Scale . X * logoHoverContainer . Scale . X * 0.74f ;
2016-10-07 16:07:23 +08:00
2017-03-23 12:41:50 +08:00
private readonly Sprite ripple ;
2016-10-07 16:07:23 +08:00
2017-03-23 12:41:50 +08:00
private readonly Container rippleContainer ;
2016-10-07 16:07:23 +08:00
2016-12-01 19:21:14 +08:00
public bool Triangles
{
2017-11-01 19:54:58 +08:00
set { colourAndTriangles . FadeTo ( value ? 1 : 0 , transition_length , Easing . OutQuint ) ; }
2016-12-01 19:21:14 +08:00
}
2017-06-30 14:54:03 +08:00
public override bool ReceiveMouseInputAt ( Vector2 screenSpacePos ) = > logoContainer . ReceiveMouseInputAt ( screenSpacePos ) ;
2016-10-17 01:25:08 +08:00
2016-10-07 16:07:23 +08:00
public bool Ripple
{
get { return rippleContainer . Alpha > 0 ; }
2017-11-01 19:54:58 +08:00
set { rippleContainer . FadeTo ( value ? 1 : 0 , transition_length , Easing . OutQuint ) ; }
2016-10-07 16:07:23 +08:00
}
2017-03-23 12:41:50 +08:00
private readonly Box flashLayer ;
2016-10-07 16:07:23 +08:00
2017-05-15 18:36:03 +08:00
private readonly Container impactContainer ;
private const float default_size = 480 ;
2017-06-29 01:19:04 +08:00
private const double early_activation = 60 ;
2017-05-23 11:29:43 +08:00
2016-10-07 16:07:23 +08:00
public OsuLogo ( )
{
2017-11-08 19:36:18 +08:00
// Required to make Schedule calls run in OsuScreen even when we are not visible.
2017-11-01 19:54:58 +08:00
AlwaysPresent = true ;
2017-06-29 01:19:04 +08:00
EarlyActivationMilliseconds = early_activation ;
2017-05-23 11:29:43 +08:00
2017-05-15 18:36:03 +08:00
Size = new Vector2 ( default_size ) ;
2016-10-07 16:07:23 +08:00
Origin = Anchor . Centre ;
2016-10-22 16:50:42 +08:00
AutoSizeAxes = Axes . Both ;
2016-10-07 16:07:23 +08:00
Children = new Drawable [ ]
{
2017-11-03 16:54:35 +08:00
intro = new IntroSequence
{
RelativeSizeAxes = Axes . Both ,
} ,
2017-05-23 14:29:23 +08:00
logoHoverContainer = new Container
2016-10-07 16:07:23 +08:00
{
2016-10-22 16:50:42 +08:00
AutoSizeAxes = Axes . Both ,
2016-10-07 16:07:23 +08:00
Children = new Drawable [ ]
{
2017-05-23 11:29:43 +08:00
logoBounceContainer = new Container
2016-10-07 16:07:23 +08:00
{
2016-10-22 17:40:04 +08:00
AutoSizeAxes = Axes . Both ,
2016-12-01 19:21:14 +08:00
Children = new Drawable [ ]
2016-10-17 01:25:08 +08:00
{
2017-05-23 14:29:23 +08:00
rippleContainer = new Container
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
ripple = new Sprite
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2017-09-07 21:46:21 +08:00
Blending = BlendingMode . Additive ,
2017-05-23 14:29:23 +08:00
Alpha = 0
}
}
} ,
2017-05-24 00:45:01 +08:00
logoAmplitudeContainer = new Container
2016-12-01 19:21:14 +08:00
{
2016-12-01 20:29:14 +08:00
AutoSizeAxes = Axes . Both ,
2016-12-01 19:21:14 +08:00
Children = new Drawable [ ]
{
2017-05-24 00:45:01 +08:00
logoBeatContainer = new Container
2016-12-01 19:21:14 +08:00
{
2017-05-23 11:29:43 +08:00
AutoSizeAxes = Axes . Both ,
2016-12-01 19:52:26 +08:00
Children = new Drawable [ ]
{
2017-06-19 08:33:50 +08:00
visualizer = new LogoVisualisation
{
RelativeSizeAxes = Axes . Both ,
Origin = Anchor . Centre ,
Anchor = Anchor . Centre ,
Alpha = 0.5f ,
Size = new Vector2 ( 0.96f )
} ,
2017-05-24 00:45:01 +08:00
new BufferedContainer
2016-12-01 19:52:26 +08:00
{
2017-05-24 00:45:01 +08:00
AutoSizeAxes = Axes . Both ,
2016-12-01 20:29:14 +08:00
Children = new Drawable [ ]
{
2017-05-24 00:45:01 +08:00
logoContainer = new CircularContainer
2016-12-01 20:29:14 +08:00
{
2017-05-23 11:29:43 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2017-05-24 00:45:01 +08:00
RelativeSizeAxes = Axes . Both ,
Scale = new Vector2 ( 0.88f ) ,
Masking = true ,
2017-05-23 11:29:43 +08:00
Children = new Drawable [ ]
{
2017-05-24 00:45:01 +08:00
colourAndTriangles = new Container
2017-05-23 11:29:43 +08:00
{
RelativeSizeAxes = Axes . Both ,
2017-05-24 00:45:01 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
Colour = OsuPink ,
} ,
2017-05-24 12:05:11 +08:00
triangles = new Triangles
2017-05-24 00:45:01 +08:00
{
TriangleScale = 4 ,
ColourLight = OsuColour . FromHex ( @"ff7db7" ) ,
ColourDark = OsuColour . FromHex ( @"de5b95" ) ,
RelativeSizeAxes = Axes . Both ,
} ,
}
2017-05-23 11:29:43 +08:00
} ,
2017-05-24 00:45:01 +08:00
flashLayer = new Box
2017-05-23 11:29:43 +08:00
{
RelativeSizeAxes = Axes . Both ,
2017-09-07 21:46:21 +08:00
Blending = BlendingMode . Additive ,
2017-05-24 00:45:01 +08:00
Colour = Color4 . White ,
Alpha = 0 ,
2017-05-23 11:29:43 +08:00
} ,
2017-05-24 00:45:01 +08:00
} ,
2016-12-01 20:29:14 +08:00
} ,
2017-05-24 00:45:01 +08:00
logo = new Sprite
2016-12-01 20:29:14 +08:00
{
2017-05-24 00:45:01 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2016-12-01 20:29:14 +08:00
} ,
2017-05-24 00:45:01 +08:00
}
2016-12-01 19:52:26 +08:00
} ,
2017-05-24 00:45:01 +08:00
impactContainer = new CircularContainer
2017-01-27 12:50:22 +08:00
{
2017-05-23 11:29:43 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2017-05-24 00:45:01 +08:00
Alpha = 0 ,
BorderColour = Color4 . White ,
RelativeSizeAxes = Axes . Both ,
BorderThickness = 10 ,
Masking = true ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
AlwaysPresent = true ,
Alpha = 0 ,
}
}
2017-05-23 11:29:43 +08:00
}
}
2017-05-15 18:36:03 +08:00
}
}
2016-10-07 16:07:23 +08:00
}
}
}
}
}
} ;
}
2017-11-13 17:43:05 +08:00
/// <summary>
/// Schedule a new extenral animation. Handled queueing and finishing previous animations in a sane way.
/// </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>
2017-11-21 10:49:42 +08:00
public void AppendAnimatingAction ( Action action , bool waitForPrevious )
2017-11-13 17:43:05 +08:00
{
2018-01-16 02:42:17 +08:00
void runnableAction ( )
2017-11-13 17:43:05 +08:00
{
if ( waitForPrevious )
this . DelayUntilTransformsFinished ( ) . Schedule ( action ) ;
else
{
ClearTransforms ( ) ;
action ( ) ;
}
2018-01-16 02:42:17 +08:00
}
2017-11-13 17:43:05 +08:00
if ( IsLoaded )
runnableAction ( ) ;
else
2018-01-16 04:27:00 +08:00
Schedule ( runnableAction ) ;
2017-11-13 17:43:05 +08:00
}
2016-11-12 18:44:16 +08:00
[BackgroundDependencyLoader]
2017-05-24 01:09:31 +08:00
private void load ( TextureStore textures , AudioManager audio )
2016-10-07 16:07:23 +08:00
{
2017-11-25 22:11:18 +08:00
sampleClick = audio . Sample . Get ( @"Menu/osu-logo-select" ) ;
sampleBeat = audio . Sample . Get ( @"Menu/osu-logo-heartbeat" ) ;
2017-06-29 01:19:04 +08:00
2017-02-06 21:17:29 +08:00
logo . Texture = textures . Get ( @"Menu/logo" ) ;
ripple . Texture = textures . Get ( @"Menu/logo" ) ;
2016-11-01 22:24:14 +08:00
}
2017-05-24 00:45:01 +08:00
private int lastBeatIndex ;
2017-05-24 00:17:09 +08:00
protected override void OnNewBeat ( int beatIndex , TimingControlPoint timingPoint , EffectControlPoint effectPoint , TrackAmplitudes amplitudes )
2017-05-23 11:29:43 +08:00
{
2017-05-24 00:17:09 +08:00
base . OnNewBeat ( beatIndex , timingPoint , effectPoint , amplitudes ) ;
2017-05-23 11:29:43 +08:00
2017-05-24 00:45:01 +08:00
lastBeatIndex = beatIndex ;
2017-05-24 00:17:09 +08:00
var beatLength = timingPoint . BeatLength ;
2016-10-07 16:07:23 +08:00
2017-05-24 00:45:01 +08:00
float amplitudeAdjust = Math . Min ( 1 , 0.4f + amplitudes . Maximum ) ;
2017-05-24 00:17:09 +08:00
if ( beatIndex < 0 ) return ;
2017-05-23 14:29:23 +08:00
2017-07-07 13:59:17 +08:00
if ( IsHovered )
2017-07-16 22:37:59 +08:00
this . Delay ( early_activation ) . Schedule ( ( ) = > sampleBeat . Play ( ) ) ;
2017-06-24 04:57:57 +08:00
2017-07-16 23:28:20 +08:00
logoBeatContainer
2017-07-23 02:50:25 +08:00
. ScaleTo ( 1 - 0.02f * amplitudeAdjust , early_activation , Easing . Out )
2017-07-16 23:28:20 +08:00
. Then ( )
2017-07-23 02:50:25 +08:00
. ScaleTo ( 1 , beatLength * 2 , Easing . OutQuint ) ;
2017-05-23 14:29:23 +08:00
ripple . ClearTransforms ( ) ;
2017-07-16 23:28:20 +08:00
ripple
. ScaleTo ( logoAmplitudeContainer . Scale )
2017-07-23 02:50:25 +08:00
. ScaleTo ( logoAmplitudeContainer . Scale * ( 1 + 0.04f * amplitudeAdjust ) , beatLength , Easing . OutQuint )
. FadeTo ( 0.15f * amplitudeAdjust ) . FadeOut ( beatLength , Easing . OutQuint ) ;
2017-05-23 15:27:01 +08:00
2017-05-24 00:17:09 +08:00
if ( effectPoint . KiaiMode & & flashLayer . Alpha < 0.4f )
2017-05-23 15:27:01 +08:00
{
flashLayer . ClearTransforms ( ) ;
2017-07-16 23:28:20 +08:00
flashLayer
2017-07-23 02:50:25 +08:00
. FadeTo ( 0.2f * amplitudeAdjust , early_activation , Easing . Out )
2017-07-16 23:28:20 +08:00
. Then ( )
. FadeOut ( beatLength ) ;
2017-05-23 15:27:01 +08:00
2017-07-16 23:28:20 +08:00
visualizer . ClearTransforms ( ) ;
visualizer
2017-07-23 02:50:25 +08:00
. FadeTo ( 0.9f * amplitudeAdjust , early_activation , Easing . Out )
2017-07-16 23:28:20 +08:00
. Then ( )
. FadeTo ( 0.5f , beatLength ) ;
2017-05-23 15:27:01 +08:00
}
2017-05-23 11:29:43 +08:00
}
2017-11-03 16:54:35 +08:00
public void PlayIntro ( )
{
2017-11-08 13:31:11 +08:00
const double length = 3150 ;
const double fade = 200 ;
2017-11-03 16:54:35 +08:00
logoHoverContainer . FadeOut ( ) . Delay ( length ) . FadeIn ( fade ) ;
intro . Show ( ) ;
intro . Start ( length ) ;
intro . Delay ( length + fade ) . FadeOut ( ) ;
}
2017-05-24 00:45:01 +08:00
protected override void Update ( )
{
base . Update ( ) ;
2017-05-24 12:05:28 +08:00
const float scale_adjust_cutoff = 0.4f ;
2017-05-24 12:05:11 +08:00
const float velocity_adjust_cutoff = 0.98f ;
2017-09-11 17:31:36 +08:00
const float paused_velocity = 0.5f ;
2017-05-24 12:05:28 +08:00
2017-09-11 17:31:36 +08:00
if ( Beatmap . Value . Track . IsRunning )
{
var maxAmplitude = lastBeatIndex > = 0 ? Beatmap . Value . Track . CurrentAmplitudes . Maximum : 0 ;
logoAmplitudeContainer . ScaleTo ( 1 - Math . Max ( 0 , maxAmplitude - scale_adjust_cutoff ) * 0.04f , 75 , Easing . OutQuint ) ;
2017-05-24 12:05:11 +08:00
2017-09-11 17:31:36 +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 ) ;
}
2017-05-24 12:05:11 +08:00
else
2017-09-11 17:31:36 +08:00
{
triangles . Velocity = paused_velocity ;
}
2017-05-24 00:45:01 +08:00
}
2017-11-01 19:54:58 +08:00
private bool interactive = > Action ! = null & & Alpha > 0.2f ;
2016-10-07 16:07:23 +08:00
protected override bool OnMouseDown ( InputState state , MouseDownEventArgs args )
{
2017-11-01 19:54:58 +08:00
if ( ! interactive ) return false ;
2016-10-07 16:07:23 +08:00
2017-07-23 02:50:25 +08:00
logoBounceContainer . ScaleTo ( 0.9f , 1000 , Easing . Out ) ;
2016-10-07 16:07:23 +08:00
return true ;
}
protected override bool OnMouseUp ( InputState state , MouseUpEventArgs args )
{
2017-07-23 02:50:25 +08:00
logoBounceContainer . ScaleTo ( 1f , 500 , Easing . OutElastic ) ;
2016-10-07 16:07:23 +08:00
return true ;
}
protected override bool OnClick ( InputState state )
{
2017-11-01 19:54:58 +08:00
if ( ! interactive ) return false ;
2016-10-07 16:07:23 +08:00
2017-11-25 22:29:08 +08:00
if ( Action ? . Invoke ( ) ? ? true )
sampleClick . Play ( ) ;
2017-02-07 20:45:59 +08:00
2017-02-25 20:12:39 +08:00
flashLayer . ClearTransforms ( ) ;
2017-01-27 12:50:22 +08:00
flashLayer . Alpha = 0.4f ;
2017-07-23 02:50:25 +08:00
flashLayer . FadeOut ( 1500 , Easing . OutExpo ) ;
2016-10-07 16:07:23 +08:00
return true ;
}
protected override bool OnHover ( InputState state )
{
2017-11-01 19:54:58 +08:00
if ( ! interactive ) return false ;
2017-05-15 18:36:03 +08:00
2017-07-23 02:50:25 +08:00
logoHoverContainer . ScaleTo ( 1.1f , 500 , Easing . OutElastic ) ;
2016-10-07 16:07:23 +08:00
return true ;
}
protected override void OnHoverLost ( InputState state )
{
2017-07-23 02:50:25 +08:00
logoHoverContainer . ScaleTo ( 1 , 500 , Easing . OutElastic ) ;
2016-10-07 16:07:23 +08:00
}
2017-05-15 18:36:03 +08:00
public void Impact ( )
{
2017-07-23 02:50:25 +08:00
impactContainer . FadeOutFromOne ( 250 , Easing . In ) ;
2017-05-15 18:36:03 +08:00
impactContainer . ScaleTo ( 0.96f ) ;
impactContainer . ScaleTo ( 1.12f , 250 ) ;
}
2016-10-07 16:07:23 +08:00
}
2017-05-24 01:53:21 +08:00
}