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
2019-04-08 17:32:05 +08:00
using System.Collections.Generic ;
2018-05-28 12:02:06 +08:00
using Microsoft.EntityFrameworkCore.Internal ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
2018-04-30 01:15:09 +08:00
using osu.Framework.Audio ;
using osu.Framework.Audio.Sample ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2018-04-30 01:15:09 +08:00
using osu.Framework.Graphics ;
2018-05-15 01:27:05 +08:00
using osu.Framework.Input.Bindings ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Screens ;
using osu.Game.Beatmaps ;
2018-05-15 01:27:05 +08:00
using osu.Game.Input.Bindings ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets ;
using osu.Game.Screens.Menu ;
2018-05-28 19:43:47 +08:00
using osu.Game.Overlays ;
2019-04-13 04:36:01 +08:00
using osu.Game.Users ;
using osu.Game.Online.API ;
2019-04-08 17:32:05 +08:00
using osu.Game.Rulesets.Mods ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Screens
{
2019-01-23 19:52:00 +08:00
public abstract class OsuScreen : Screen , IOsuScreen , IKeyBindingHandler < GlobalAction > , IHasDescription
2018-04-13 17:19:50 +08:00
{
2019-01-25 13:10:59 +08:00
/// <summary>
/// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen.
/// This allows for the game content to be pushed byt he options/notification overlays without causing black areas to appear.
/// </summary>
public const float HORIZONTAL_OVERFLOW_PADDING = 50 ;
2018-05-28 12:02:06 +08:00
/// <summary>
/// A user-facing title for this screen.
/// </summary>
public virtual string Title = > GetType ( ) . ShortDisplayName ( ) ;
public string Description = > Title ;
2018-05-05 02:18:48 +08:00
protected virtual bool AllowBackButton = > true ;
2018-11-29 16:18:59 +08:00
public virtual bool AllowExternalScreenChange = > false ;
2018-04-13 17:19:50 +08:00
/// <summary>
2018-05-28 19:43:47 +08:00
/// Whether all overlays should be hidden when this screen is entered or resumed.
2018-04-13 17:19:50 +08:00
/// </summary>
2019-01-28 14:41:54 +08:00
public virtual bool HideOverlaysOnEnter = > false ;
2018-05-21 21:53:50 +08:00
/// <summary>
2018-06-03 17:30:58 +08:00
/// Whether overlays should be able to be opened once this screen is entered or resumed.
2018-05-21 21:53:50 +08:00
/// </summary>
2019-01-28 14:41:54 +08:00
public virtual OverlayActivation InitialOverlayActivationMode = > OverlayActivation . All ;
2018-04-13 17:19:50 +08:00
public virtual bool CursorVisible = > true ;
protected new OsuGameBase Game = > base . Game as OsuGameBase ;
2019-04-13 04:36:01 +08:00
/// <summary>
/// The <see cref="UserStatus"/> to set the user's status automatically to when this screen is entered / resumed.
/// Note that the user status won't be automatically set if :
/// <para>- <see cref="ScreenStatus"/> is overriden and returns null</para>
/// <para>- The current <see cref="UserStatus"/> is <see cref="UserStatusDoNotDisturb"/> or <see cref="UserStatusOffline"/></para>
/// </summary>
protected virtual UserStatus ScreenStatus = > new UserStatusOnline ( ) ;
2019-02-01 14:42:15 +08:00
/// <summary>
2019-02-13 10:34:48 +08:00
/// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children).
2019-02-01 14:42:15 +08:00
/// </summary>
public virtual bool DisallowExternalBeatmapRulesetChanges = > false ;
private SampleChannel sampleExit ;
2018-04-13 17:19:50 +08:00
2019-01-23 19:52:00 +08:00
public virtual float BackgroundParallaxAmount = > 1 ;
2018-04-13 17:19:50 +08:00
2019-04-08 18:16:34 +08:00
public Bindable < WorkingBeatmap > Beatmap { get ; private set ; }
2019-02-02 16:11:25 +08:00
2019-04-08 18:16:34 +08:00
public Bindable < RulesetInfo > Ruleset { get ; private set ; }
2018-04-13 17:19:50 +08:00
2019-04-10 16:13:12 +08:00
public Bindable < IReadOnlyList < Mod > > Mods { get ; private set ; }
2018-04-13 17:19:50 +08:00
2019-02-02 16:11:25 +08:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
{
2019-04-09 11:58:10 +08:00
var screenDependencies = new OsuScreenDependencies ( DisallowExternalBeatmapRulesetChanges , parent ) ;
2019-02-01 14:42:15 +08:00
2019-04-09 11:53:00 +08:00
Beatmap = screenDependencies . Beatmap ;
Ruleset = screenDependencies . Ruleset ;
2019-04-10 11:03:57 +08:00
Mods = screenDependencies . Mods ;
2019-02-01 14:42:15 +08:00
2019-04-09 11:53:00 +08:00
return base . CreateChildDependencies ( screenDependencies ) ;
2019-02-02 16:11:25 +08:00
}
2018-04-13 17:19:50 +08:00
2019-01-31 17:10:21 +08:00
protected BackgroundScreen Background = > backgroundStack ? . CurrentScreen as BackgroundScreen ;
2019-01-25 14:36:22 +08:00
private BackgroundScreen localBackground ;
2019-01-31 10:00:33 +08:00
[Resolved(canBeNull: true)]
2019-01-25 14:36:22 +08:00
private BackgroundScreenStack backgroundStack { get ; set ; }
2019-01-31 10:00:33 +08:00
[Resolved(canBeNull: true)]
2019-01-25 14:36:22 +08:00
private OsuLogo logo { get ; set ; }
2019-05-01 03:40:44 +08:00
private IAPIProvider api ;
2019-04-13 04:36:01 +08:00
2019-01-23 19:52:00 +08:00
protected OsuScreen ( )
{
Anchor = Anchor . Centre ;
Origin = Anchor . Centre ;
}
2018-06-06 19:19:53 +08:00
[BackgroundDependencyLoader(true)]
2019-05-01 03:40:44 +08:00
private void load ( OsuGame osu , AudioManager audio , IAPIProvider provider )
2018-04-13 17:19:50 +08:00
{
2018-08-31 06:04:40 +08:00
sampleExit = audio . Sample . Get ( @"UI/screen-back" ) ;
2019-05-01 03:40:44 +08:00
api = provider ;
2018-04-13 17:19:50 +08:00
}
2018-07-03 17:37:21 +08:00
public virtual bool OnPressed ( GlobalAction action )
2018-04-13 17:19:50 +08:00
{
2019-01-23 19:52:00 +08:00
if ( ! this . IsCurrentScreen ( ) ) return false ;
2018-06-27 15:06:26 +08:00
2018-05-15 01:27:05 +08:00
if ( action = = GlobalAction . Back & & AllowBackButton )
2018-04-13 17:19:50 +08:00
{
2019-01-23 19:52:00 +08:00
this . Exit ( ) ;
2018-05-05 02:18:48 +08:00
return true ;
2018-04-13 17:19:50 +08:00
}
2018-05-15 01:27:05 +08:00
return false ;
2018-04-13 17:19:50 +08:00
}
2018-05-15 01:27:05 +08:00
public bool OnReleased ( GlobalAction action ) = > action = = GlobalAction . Back & & AllowBackButton ;
2019-01-23 19:52:00 +08:00
public override void OnResuming ( IScreen last )
2018-04-13 17:19:50 +08:00
{
sampleExit ? . Play ( ) ;
applyArrivingDefaults ( true ) ;
2019-05-01 03:40:44 +08:00
if ( api ! = null )
api . LocalUser . Value . Status . ValueChanged + = userStatusChanged ;
2019-04-13 04:36:01 +08:00
setUserStatus ( ScreenStatus ) ;
2018-04-13 17:19:50 +08:00
base . OnResuming ( last ) ;
}
2019-05-01 03:40:44 +08:00
private void userStatusChanged ( ValueChangedEvent < UserStatus > obj )
{
2019-05-03 01:44:07 +08:00
if ( obj . NewValue ? . GetType ( ) = = ScreenStatus ? . GetType ( ) ) return ; //don't update the user's status if the current status is of the same type as the given one
setUserStatus ( ScreenStatus ) ;
2019-05-01 03:40:44 +08:00
}
2019-01-23 19:52:00 +08:00
public override void OnSuspending ( IScreen next )
2018-04-13 17:19:50 +08:00
{
base . OnSuspending ( next ) ;
2019-05-01 03:40:44 +08:00
if ( api ! = null )
api . LocalUser . Value . Status . ValueChanged - = userStatusChanged ;
2018-04-13 17:19:50 +08:00
onSuspendingLogo ( ) ;
}
2019-01-23 19:52:00 +08:00
public override void OnEntering ( IScreen last )
2018-04-13 17:19:50 +08:00
{
applyArrivingDefaults ( false ) ;
2019-01-31 10:00:33 +08:00
backgroundStack ? . Push ( localBackground = CreateBackground ( ) ) ;
2019-01-25 14:36:22 +08:00
2019-05-01 03:40:44 +08:00
if ( api ! = null )
api . LocalUser . Value . Status . ValueChanged + = userStatusChanged ;
2019-04-13 04:36:01 +08:00
setUserStatus ( ScreenStatus ) ;
2018-04-13 17:19:50 +08:00
base . OnEntering ( last ) ;
}
2019-01-23 19:52:00 +08:00
public override bool OnExiting ( IScreen next )
2018-04-13 17:19:50 +08:00
{
if ( ValidForResume & & logo ! = null )
onExitingLogo ( ) ;
2019-05-01 03:40:44 +08:00
if ( api ! = null )
api . LocalUser . Value . Status . ValueChanged - = userStatusChanged ;
2018-04-13 17:19:50 +08:00
if ( base . OnExiting ( next ) )
return true ;
2019-01-31 17:10:21 +08:00
if ( localBackground ! = null & & backgroundStack ? . CurrentScreen = = localBackground )
backgroundStack ? . Exit ( ) ;
2019-01-25 14:36:22 +08:00
2018-04-13 17:19:50 +08:00
return false ;
}
2019-04-13 04:36:01 +08:00
private void setUserStatus ( UserStatus status )
{
2019-05-03 01:44:07 +08:00
if ( api = = null ) return ;
if ( status = = null ) return ;
if ( ! ( api . LocalUser . Value . Status . Value is UserStatusOnline ) ) return ; //don't update the user's status if the current status doesn't allow to be modified by screens (eg: DND / Offline)
api . LocalUser . Value . Status . Value = status ;
2019-04-13 04:36:01 +08:00
}
2018-04-13 17:19:50 +08:00
/// <summary>
/// Fired when this screen was entered or resumed and the logo state is required to be adjusted.
/// </summary>
protected virtual void LogoArriving ( OsuLogo logo , bool resuming )
{
2019-01-25 20:02:35 +08:00
ApplyLogoArrivingDefaults ( logo ) ;
2018-04-13 17:19:50 +08:00
}
private void applyArrivingDefaults ( bool isResuming )
{
2019-01-31 10:00:33 +08:00
logo ? . AppendAnimatingAction ( ( ) = >
2018-05-30 18:25:39 +08:00
{
2019-01-23 19:52:00 +08:00
if ( this . IsCurrentScreen ( ) ) LogoArriving ( logo , isResuming ) ;
2018-05-30 18:25:39 +08:00
} , true ) ;
2018-04-13 17:19:50 +08:00
}
2019-01-25 20:02:35 +08:00
/// <summary>
/// Applies default animations to an arriving logo.
/// Todo: This should not exist.
/// </summary>
/// <param name="logo">The logo to apply animations to.</param>
public static void ApplyLogoArrivingDefaults ( OsuLogo logo )
{
logo . Action = null ;
logo . FadeOut ( 300 , Easing . OutQuint ) ;
logo . Anchor = Anchor . TopLeft ;
logo . Origin = Anchor . Centre ;
2019-02-20 18:33:14 +08:00
logo . RelativePositionAxes = Axes . Both ;
2019-01-25 20:02:35 +08:00
logo . BeatMatching = true ;
logo . Triangles = true ;
logo . Ripple = true ;
}
2018-04-13 17:19:50 +08:00
private void onExitingLogo ( )
{
2019-01-31 10:00:33 +08:00
logo ? . AppendAnimatingAction ( ( ) = > LogoExiting ( logo ) , false ) ;
2018-04-13 17:19:50 +08:00
}
/// <summary>
/// Fired when this screen was exited to add any outwards transition to the logo.
/// </summary>
protected virtual void LogoExiting ( OsuLogo logo )
{
}
private void onSuspendingLogo ( )
{
2019-01-31 10:00:33 +08:00
logo ? . AppendAnimatingAction ( ( ) = > LogoSuspending ( logo ) , false ) ;
2018-04-13 17:19:50 +08:00
}
/// <summary>
/// Fired when this screen was suspended to add any outwards transition to the logo.
/// </summary>
protected virtual void LogoSuspending ( OsuLogo logo )
{
}
2019-01-25 14:36:22 +08:00
/// <summary>
/// Override to create a BackgroundMode for the current screen.
/// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause.
/// </summary>
protected virtual BackgroundScreen CreateBackground ( ) = > null ;
2018-04-13 17:19:50 +08:00
}
}