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
2018-12-26 21:16:35 +08:00
using System ;
2020-12-10 15:56:56 +08:00
using System.Diagnostics ;
2018-04-13 17:19:50 +08:00
using System.Threading.Tasks ;
2021-11-10 16:08:51 +08:00
using ManagedBass.Fx ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
2019-09-15 21:59:46 +08:00
using osu.Framework.Audio ;
2019-10-01 23:39:01 +08:00
using osu.Framework.Bindables ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Sprites ;
2020-10-20 06:22:30 +08:00
using osu.Framework.Graphics.Transforms ;
2019-03-20 18:35:40 +08:00
using osu.Framework.Input ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Screens ;
2018-05-09 22:31:52 +08:00
using osu.Framework.Threading ;
2024-02-16 16:13:41 +08:00
using osu.Framework.Utils ;
2023-07-07 14:36:17 +08:00
using osu.Game.Audio ;
2021-10-07 13:15:16 +08:00
using osu.Game.Audio.Effects ;
2019-10-01 23:39:01 +08:00
using osu.Game.Configuration ;
2018-04-13 17:19:50 +08:00
using osu.Game.Graphics ;
2019-03-22 18:01:32 +08:00
using osu.Game.Graphics.Containers ;
2019-07-05 14:32:07 +08:00
using osu.Game.Input ;
2023-01-14 04:11:25 +08:00
using osu.Game.Localisation ;
2019-09-15 21:59:46 +08:00
using osu.Game.Overlays ;
using osu.Game.Overlays.Notifications ;
2024-02-26 21:13:15 +08:00
using osu.Game.Performance ;
2018-04-13 17:19:50 +08:00
using osu.Game.Screens.Menu ;
using osu.Game.Screens.Play.PlayerSettings ;
2023-07-07 14:36:17 +08:00
using osu.Game.Skinning ;
2019-04-13 19:18:44 +08:00
using osu.Game.Users ;
2021-04-09 07:34:35 +08:00
using osu.Game.Utils ;
2018-11-20 15:51:59 +08:00
using osuTK ;
using osuTK.Graphics ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Screens.Play
{
2022-11-24 13:32:20 +08:00
public partial class PlayerLoader : ScreenWithBeatmapBackground
2018-04-13 17:19:50 +08:00
{
2019-03-14 15:09:17 +08:00
protected const float BACKGROUND_BLUR = 15 ;
2019-03-13 15:47:03 +08:00
2021-11-12 19:30:40 +08:00
protected const double CONTENT_OUT_DURATION = 300 ;
protected virtual double PlayerPushDelay = > 1800 ;
2021-10-13 12:26:20 +08:00
2020-02-14 17:22:57 +08:00
public override bool HideOverlaysOnEnter = > hideOverlays ;
2018-04-13 17:19:50 +08:00
2020-02-14 17:22:57 +08:00
public override bool DisallowExternalBeatmapRulesetChanges = > true ;
2019-01-24 18:55:42 +08:00
2023-07-30 12:45:42 +08:00
public override bool? AllowGlobalTrackControl = > false ;
2020-02-14 17:22:57 +08:00
// Here because IsHovered will not update unless we do so.
public override bool HandlePositionalInput = > true ;
2018-04-13 17:19:50 +08:00
2020-02-14 17:22:57 +08:00
// We show the previous screen status
2022-03-17 18:36:33 +08:00
protected override UserActivity ? InitialActivity = > null ;
2018-04-13 17:19:50 +08:00
2022-03-17 18:36:33 +08:00
protected BeatmapMetadataDisplay MetadataInfo { get ; private set ; } = null ! ;
2019-02-01 14:42:15 +08:00
2021-08-13 11:27:57 +08:00
/// <summary>
/// A fill flow containing the player settings groups, exposed for the ability to hide it from inheritors of the player loader.
/// </summary>
2022-03-17 18:36:33 +08:00
protected FillFlowContainer < PlayerSettingsGroup > PlayerSettings { get ; private set ; } = null ! ;
2021-08-13 11:27:57 +08:00
2022-03-17 18:36:33 +08:00
protected VisualSettings VisualSettings { get ; private set ; } = null ! ;
2019-04-25 13:15:07 +08:00
2022-03-17 18:36:33 +08:00
protected AudioSettings AudioSettings { get ; private set ; } = null ! ;
2022-03-01 15:14:57 +08:00
2022-03-17 18:36:33 +08:00
protected Task ? LoadTask { get ; private set ; }
2019-12-06 12:47:34 +08:00
2022-03-17 18:36:33 +08:00
protected Task ? DisposalTask { get ; private set ; }
2018-04-13 17:19:50 +08:00
2022-10-13 16:12:28 +08:00
private OsuScrollContainer settingsScroll = null ! ;
2023-03-14 17:32:24 +08:00
private Bindable < bool > showStoryboards = null ! ;
2020-02-14 17:22:57 +08:00
private bool backgroundBrightnessReduction ;
2020-10-25 19:33:35 +08:00
private readonly BindableDouble volumeAdjustment = new BindableDouble ( 1 ) ;
2024-02-26 21:16:15 +08:00
private AudioFilter ? lowPassFilter ;
private AudioFilter ? highPassFilter ;
2021-10-07 13:15:16 +08:00
2023-07-07 14:36:17 +08:00
private SkinnableSound sampleRestart = null ! ;
2022-10-13 15:04:38 +08:00
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider ( OverlayColourScheme . Purple ) ;
2020-02-14 17:22:57 +08:00
protected bool BackgroundBrightnessReduction
{
set
{
if ( value = = backgroundBrightnessReduction )
return ;
backgroundBrightnessReduction = value ;
2021-01-04 17:32:23 +08:00
ApplyToBackground ( b = > b . FadeColour ( OsuColour . Gray ( backgroundBrightnessReduction ? 0.8f : 1 ) , 200 ) ) ;
2020-02-14 17:22:57 +08:00
}
}
private bool readyForPush = >
2020-12-10 15:56:56 +08:00
! playerConsumed
2020-02-14 18:02:37 +08:00
// don't push unless the player is completely loaded
2022-03-17 18:23:43 +08:00
& & CurrentPlayer ? . LoadState = = LoadState . Ready
2022-04-21 21:55:13 +08:00
// don't push unless the player is ready to start gameplay
& & ReadyForGameplay ;
protected virtual bool ReadyForGameplay = >
// not ready if the user is hovering one of the panes, unless they are idle.
( IsHovered | | idleTracker . IsIdle . Value )
// not ready if the user is dragging a slider or otherwise.
2022-03-17 18:36:33 +08:00
& & inputManager . DraggedDrawable = = null
2022-04-21 21:55:13 +08:00
// not ready if a focused overlay is visible, like settings.
2022-03-17 18:36:33 +08:00
& & inputManager . FocusedDrawable = = null ;
2020-02-14 17:22:57 +08:00
private readonly Func < Player > createPlayer ;
2022-03-17 18:23:43 +08:00
/// <summary>
/// The <see cref="Player"/> instance being loaded by this screen.
/// </summary>
2022-03-17 18:36:33 +08:00
public Player ? CurrentPlayer { get ; private set ; }
2020-02-14 17:22:57 +08:00
2020-12-10 15:56:56 +08:00
/// <summary>
2022-03-17 18:23:43 +08:00
/// Whether the current player instance has been consumed via <see cref="consumePlayer"/>.
2020-12-10 15:56:56 +08:00
/// </summary>
private bool playerConsumed ;
2022-03-17 18:36:33 +08:00
private LogoTrackingContainer content = null ! ;
2020-02-14 17:22:57 +08:00
private bool hideOverlays ;
2022-03-17 18:36:33 +08:00
private InputManager inputManager = null ! ;
2020-02-14 17:22:57 +08:00
2022-03-17 18:36:33 +08:00
private IdleTracker idleTracker = null ! ;
2019-09-15 22:32:23 +08:00
2022-03-17 18:36:33 +08:00
private ScheduledDelegate ? scheduledPushPlayer ;
2020-02-14 17:22:57 +08:00
2022-03-17 18:36:33 +08:00
private EpilepsyWarning ? epilepsyWarning ;
2020-10-20 06:08:05 +08:00
2022-08-16 12:04:56 +08:00
private bool quickRestart ;
2022-08-06 23:02:45 +08:00
2024-02-26 21:13:15 +08:00
private IDisposable ? highPerformanceSession ;
2023-01-14 04:11:50 +08:00
[Resolved]
2022-04-19 05:18:10 +08:00
private INotificationOverlay ? notificationOverlay { get ; set ; }
2019-10-03 18:16:31 +08:00
2023-01-14 04:11:50 +08:00
[Resolved]
2022-03-17 18:36:33 +08:00
private VolumeOverlay ? volumeOverlay { get ; set ; }
2019-10-03 18:16:31 +08:00
[Resolved]
2022-03-17 18:36:33 +08:00
private AudioManager audioManager { get ; set ; } = null ! ;
2019-10-03 18:16:31 +08:00
2023-01-14 04:11:50 +08:00
[Resolved]
2022-03-17 18:36:33 +08:00
private BatteryInfo ? batteryInfo { get ; set ; }
2021-04-09 07:34:35 +08:00
2024-02-26 21:13:15 +08:00
[Resolved]
2024-02-27 18:36:03 +08:00
private IHighPerformanceSessionManager ? highPerformanceSessionManager { get ; set ; }
2024-02-26 21:13:15 +08:00
2018-12-26 21:16:35 +08:00
public PlayerLoader ( Func < Player > createPlayer )
2018-04-13 17:19:50 +08:00
{
2018-12-26 21:16:35 +08:00
this . createPlayer = createPlayer ;
}
2018-04-13 17:19:50 +08:00
[BackgroundDependencyLoader]
2024-02-26 21:16:15 +08:00
private void load ( SessionStatics sessionStatics , OsuConfigManager config )
2018-04-13 17:19:50 +08:00
{
2019-10-01 23:39:01 +08:00
muteWarningShownOnce = sessionStatics . GetBindable < bool > ( Static . MutedAudioNotificationShownOnce ) ;
2021-04-10 05:55:41 +08:00
batteryWarningShownOnce = sessionStatics . GetBindable < bool > ( Static . LowBatteryNotificationShownOnce ) ;
2023-03-14 17:32:24 +08:00
showStoryboards = config . GetBindable < bool > ( OsuSetting . ShowStoryboard ) ;
2019-10-01 23:39:01 +08:00
2022-03-03 15:23:30 +08:00
const float padding = 25 ;
2021-10-13 12:26:20 +08:00
InternalChildren = new Drawable [ ]
2019-03-27 17:11:12 +08:00
{
2021-10-13 12:26:20 +08:00
( content = new LogoTrackingContainer
2018-05-09 22:31:52 +08:00
{
2019-03-27 17:11:12 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2021-10-13 12:26:20 +08:00
RelativeSizeAxes = Axes . Both ,
} ) . WithChildren ( new Drawable [ ]
2019-03-27 17:11:12 +08:00
{
2021-10-13 12:26:20 +08:00
MetadataInfo = new BeatmapMetadataDisplay ( Beatmap . Value , Mods , content . LogoFacade )
2019-01-24 18:55:42 +08:00
{
2021-10-13 12:26:20 +08:00
Alpha = 0 ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ,
2022-10-13 16:12:28 +08:00
} ) ,
settingsScroll = new OsuScrollContainer
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
RelativeSizeAxes = Axes . Y ,
Width = SettingsToolboxGroup . CONTAINER_WIDTH + padding * 2 ,
Padding = new MarginPadding { Vertical = padding } ,
Masking = false ,
Child = PlayerSettings = new FillFlowContainer < PlayerSettingsGroup >
2021-10-13 12:26:20 +08:00
{
2022-10-13 16:12:28 +08:00
AutoSizeAxes = Axes . Both ,
Direction = FillDirection . Vertical ,
Spacing = new Vector2 ( 0 , 20 ) ,
Padding = new MarginPadding { Horizontal = padding } ,
Children = new PlayerSettingsGroup [ ]
2021-10-13 12:26:20 +08:00
{
2022-10-13 16:12:28 +08:00
VisualSettings = new VisualSettings ( ) ,
AudioSettings = new AudioSettings ( ) ,
new InputSettings ( )
}
2021-10-13 12:26:20 +08:00
} ,
2022-10-13 16:12:28 +08:00
} ,
idleTracker = new IdleTracker ( 750 ) ,
2023-07-07 14:36:17 +08:00
sampleRestart = new SkinnableSound ( new SampleInfo ( @"Gameplay/restart" , @"pause-retry-click" ) )
2021-10-13 12:26:20 +08:00
} ;
2020-10-20 06:08:05 +08:00
if ( Beatmap . Value . BeatmapInfo . EpilepsyWarning )
{
AddInternal ( epilepsyWarning = new EpilepsyWarning
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ) ;
}
2018-04-13 17:19:50 +08:00
}
2019-10-03 18:16:31 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
inputManager = GetContainingInputManager ( ) ;
2019-12-27 18:36:48 +08:00
}
2020-02-14 17:28:58 +08:00
#region Screen handling
2022-04-21 23:52:44 +08:00
public override void OnEntering ( ScreenTransitionEvent e )
2019-12-27 18:36:48 +08:00
{
2022-04-21 23:52:44 +08:00
base . OnEntering ( e ) ;
2019-12-27 18:36:48 +08:00
2021-01-04 17:32:23 +08:00
ApplyToBackground ( b = >
{
if ( epilepsyWarning ! = null )
epilepsyWarning . DimmableBackground = b ;
} ) ;
2020-10-25 19:33:35 +08:00
Beatmap . Value . Track . AddAdjustment ( AdjustableProperty . Volume , volumeAdjustment ) ;
2020-10-20 14:03:12 +08:00
2022-10-18 14:01:53 +08:00
// Start off-screen.
settingsScroll . MoveToX ( settingsScroll . DrawWidth ) ;
2019-12-27 18:36:48 +08:00
content . ScaleTo ( 0.7f ) ;
contentIn ( ) ;
2023-09-27 18:16:23 +08:00
MetadataInfo . Delay ( 750 ) . FadeIn ( 500 , Easing . OutQuint ) ;
2020-12-10 15:33:28 +08:00
// after an initial delay, start the debounced load check.
// this will continue to execute even after resuming back on restart.
2021-11-12 19:30:40 +08:00
Scheduler . Add ( new ScheduledDelegate ( pushWhenLoaded , Clock . CurrentTime + PlayerPushDelay , 0 ) ) ;
2019-10-03 18:16:31 +08:00
2020-02-14 17:28:58 +08:00
showMuteWarningIfNeeded ( ) ;
2021-04-08 02:41:21 +08:00
showBatteryWarningIfNeeded ( ) ;
2019-10-03 18:16:31 +08:00
}
2018-08-02 18:47:50 +08:00
2022-04-21 23:52:44 +08:00
public override void OnResuming ( ScreenTransitionEvent e )
2018-04-13 17:19:50 +08:00
{
2022-04-21 23:52:44 +08:00
base . OnResuming ( e ) ;
2018-04-13 17:19:50 +08:00
2022-03-17 18:36:33 +08:00
Debug . Assert ( CurrentPlayer ! = null ) ;
2024-02-26 21:13:15 +08:00
highPerformanceSession ? . Dispose ( ) ;
highPerformanceSession = null ;
2020-12-10 15:56:56 +08:00
// prepare for a retry.
2022-03-17 18:23:43 +08:00
CurrentPlayer = null ;
2020-12-10 15:56:56 +08:00
playerConsumed = false ;
2020-12-10 15:34:58 +08:00
cancelLoad ( ) ;
2020-12-10 15:56:56 +08:00
2023-07-07 14:36:17 +08:00
sampleRestart . Play ( ) ;
2018-04-13 17:19:50 +08:00
contentIn ( ) ;
}
2022-04-21 23:52:44 +08:00
public override void OnSuspending ( ScreenTransitionEvent e )
2018-12-26 21:16:35 +08:00
{
2022-04-21 23:52:44 +08:00
base . OnSuspending ( e ) ;
2018-12-26 21:16:35 +08:00
2020-02-14 17:22:57 +08:00
BackgroundBrightnessReduction = false ;
2020-10-25 19:33:35 +08:00
// we're moving to player, so a period of silence is upcoming.
// stop the track before removing adjustment to avoid a volume spike.
Beatmap . Value . Track . Stop ( ) ;
Beatmap . Value . Track . RemoveAdjustment ( AdjustableProperty . Volume , volumeAdjustment ) ;
2024-02-26 21:16:15 +08:00
lowPassFilter ? . RemoveAndDisposeImmediately ( ) ;
highPassFilter ? . RemoveAndDisposeImmediately ( ) ;
2018-12-26 21:16:35 +08:00
}
2022-04-21 23:52:44 +08:00
public override bool OnExiting ( ScreenExitEvent e )
2018-04-13 17:19:50 +08:00
{
2020-02-14 17:22:57 +08:00
cancelLoad ( ) ;
2021-11-12 19:30:40 +08:00
ContentOut ( ) ;
2018-04-13 17:19:50 +08:00
2021-10-18 12:30:37 +08:00
// If the load sequence was interrupted, the epilepsy warning may already be displayed (or in the process of being displayed).
epilepsyWarning ? . Hide ( ) ;
2021-10-13 12:26:20 +08:00
// Ensure the screen doesn't expire until all the outwards fade operations have completed.
2021-11-12 19:30:40 +08:00
this . Delay ( CONTENT_OUT_DURATION ) . FadeOut ( ) ;
2019-03-28 15:29:35 +08:00
2021-04-13 14:24:35 +08:00
ApplyToBackground ( b = > b . IgnoreUserSettings . Value = true ) ;
2021-01-04 17:32:23 +08:00
2020-02-14 17:22:57 +08:00
BackgroundBrightnessReduction = false ;
2020-10-25 19:33:35 +08:00
Beatmap . Value . Track . RemoveAdjustment ( AdjustableProperty . Volume , volumeAdjustment ) ;
2020-02-14 17:22:57 +08:00
2024-02-26 21:13:15 +08:00
highPerformanceSession ? . Dispose ( ) ;
highPerformanceSession = null ;
2022-04-21 23:52:44 +08:00
return base . OnExiting ( e ) ;
2018-04-13 17:19:50 +08:00
}
protected override void LogoArriving ( OsuLogo logo , bool resuming )
{
base . LogoArriving ( logo , resuming ) ;
2019-03-22 19:01:58 +08:00
const double duration = 300 ;
2018-04-13 17:19:50 +08:00
2021-11-12 18:30:04 +08:00
if ( ! resuming ) logo . MoveTo ( new Vector2 ( 0.5f ) , duration , Easing . OutQuint ) ;
2019-03-28 15:09:42 +08:00
2021-11-12 18:30:04 +08:00
logo . ScaleTo ( new Vector2 ( 0.15f ) , duration , Easing . OutQuint ) ;
2018-04-13 17:19:50 +08:00
logo . FadeIn ( 350 ) ;
2019-04-24 15:20:51 +08:00
Scheduler . AddDelayed ( ( ) = >
{
if ( this . IsCurrentScreen ( ) )
content . StartTracking ( logo , resuming ? 0 : 500 , Easing . InOutExpo ) ;
} , resuming ? 0 : 500 ) ;
2019-03-26 16:18:35 +08:00
}
protected override void LogoExiting ( OsuLogo logo )
{
base . LogoExiting ( logo ) ;
2019-04-08 14:24:09 +08:00
content . StopTracking ( ) ;
2018-04-13 17:19:50 +08:00
}
2022-10-13 16:29:07 +08:00
protected override void LogoSuspending ( OsuLogo logo )
{
base . LogoSuspending ( logo ) ;
content . StopTracking ( ) ;
logo
. FadeOut ( CONTENT_OUT_DURATION / 2 , Easing . OutQuint )
. ScaleTo ( logo . Scale * 0.8f , CONTENT_OUT_DURATION * 2 , Easing . OutQuint ) ;
}
2020-02-14 17:28:58 +08:00
#endregion
2020-02-14 17:22:57 +08:00
protected override void Update ( )
{
base . Update ( ) ;
2018-04-13 17:19:50 +08:00
2020-02-14 17:22:57 +08:00
if ( ! this . IsCurrentScreen ( ) )
return ;
// We need to perform this check here rather than in OnHover as any number of children of VisualSettings
// may also be handling the hover events.
if ( inputManager . HoveredDrawables . Contains ( VisualSettings ) )
{
// Preview user-defined background dim and blur when hovered on the visual settings panel.
2021-01-04 17:32:23 +08:00
ApplyToBackground ( b = >
{
2021-04-13 14:24:35 +08:00
b . IgnoreUserSettings . Value = false ;
2021-01-04 17:32:23 +08:00
b . BlurAmount . Value = 0 ;
} ) ;
2020-02-14 17:22:57 +08:00
BackgroundBrightnessReduction = false ;
}
else
{
2021-01-04 17:32:23 +08:00
ApplyToBackground ( b = >
{
// Returns background dim and blur to the values specified by PlayerLoader.
2021-04-13 14:24:35 +08:00
b . IgnoreUserSettings . Value = true ;
2021-01-04 17:32:23 +08:00
b . BlurAmount . Value = BACKGROUND_BLUR ;
} ) ;
2020-02-14 17:22:57 +08:00
BackgroundBrightnessReduction = true ;
}
}
2020-12-10 15:32:14 +08:00
private Player consumePlayer ( )
{
2020-12-10 15:56:56 +08:00
Debug . Assert ( ! playerConsumed ) ;
2022-03-17 18:36:33 +08:00
Debug . Assert ( CurrentPlayer ! = null ) ;
2020-12-10 15:56:56 +08:00
playerConsumed = true ;
2022-03-17 18:23:43 +08:00
return CurrentPlayer ;
2020-12-10 15:32:14 +08:00
}
2020-02-14 17:22:57 +08:00
private void prepareNewPlayer ( )
{
2020-08-13 11:04:32 +08:00
if ( ! this . IsCurrentScreen ( ) )
return ;
2022-03-17 18:23:43 +08:00
CurrentPlayer = createPlayer ( ) ;
2022-08-31 18:50:16 +08:00
CurrentPlayer . Configuration . AutomaticallySkipIntro | = quickRestart ;
2022-03-17 18:23:43 +08:00
CurrentPlayer . RestartCount = restartCount + + ;
CurrentPlayer . RestartRequested = restartRequested ;
2019-03-20 18:59:54 +08:00
2022-04-28 19:10:47 +08:00
LoadTask = LoadComponentAsync ( CurrentPlayer , _ = >
{
MetadataInfo . Loading = false ;
OnPlayerLoaded ( ) ;
} ) ;
}
protected virtual void OnPlayerLoaded ( )
{
2020-02-14 17:22:57 +08:00
}
2022-08-16 12:04:56 +08:00
private void restartRequested ( bool quickRestartRequested )
2020-02-14 17:22:57 +08:00
{
2022-08-16 12:04:56 +08:00
quickRestart = quickRestartRequested ;
2020-02-14 17:22:57 +08:00
hideOverlays = true ;
ValidForResume = true ;
2023-10-12 18:09:43 +08:00
this . MakeCurrent ( ) ;
2020-02-14 17:22:57 +08:00
}
private void contentIn ( )
{
2020-08-13 11:04:32 +08:00
MetadataInfo . Loading = true ;
2023-09-27 18:16:23 +08:00
content . FadeInFromZero ( 500 , Easing . OutQuint ) ;
2022-08-06 21:12:36 +08:00
content . ScaleTo ( 1 , 650 , Easing . OutQuint ) . Then ( ) . Schedule ( prepareNewPlayer ) ;
2022-10-18 14:01:53 +08:00
settingsScroll . FadeInFromZero ( 500 , Easing . Out )
. MoveToX ( 0 , 500 , Easing . OutQuint ) ;
2024-02-26 21:16:15 +08:00
AddRangeInternal ( new [ ]
{
lowPassFilter = new AudioFilter ( audioManager . TrackMixer ) ,
highPassFilter = new AudioFilter ( audioManager . TrackMixer , BQFType . HighPass ) ,
} ) ;
2021-10-13 12:26:20 +08:00
lowPassFilter . CutoffTo ( 1000 , 650 , Easing . OutQuint ) ;
2021-11-12 13:22:43 +08:00
highPassFilter . CutoffTo ( 300 ) . Then ( ) . CutoffTo ( 0 , 1250 ) ; // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in)
2021-06-09 16:17:39 +08:00
2023-02-17 03:47:51 +08:00
ApplyToBackground ( b = > b . FadeColour ( Color4 . White , 800 , Easing . OutQuint ) ) ;
2020-02-14 17:22:57 +08:00
}
2021-11-12 19:30:40 +08:00
protected virtual void ContentOut ( )
2020-02-14 17:22:57 +08:00
{
// Ensure the logo is no longer tracking before we scale the content
content . StopTracking ( ) ;
2021-11-12 19:30:40 +08:00
content . ScaleTo ( 0.7f , CONTENT_OUT_DURATION * 2 , Easing . OutQuint ) ;
2024-02-26 21:16:15 +08:00
content . FadeOut ( CONTENT_OUT_DURATION , Easing . OutQuint )
// Safety for filter potentially getting stuck in applied state due to
// transforms on `this` causing children to no longer be updated.
. OnComplete ( _ = >
{
highPassFilter ? . RemoveAndDisposeImmediately ( ) ;
highPassFilter = null ;
lowPassFilter ? . RemoveAndDisposeImmediately ( ) ;
lowPassFilter = null ;
} ) ;
2022-10-18 14:01:53 +08:00
settingsScroll . FadeOut ( CONTENT_OUT_DURATION , Easing . OutQuint )
. MoveToX ( settingsScroll . DrawWidth , CONTENT_OUT_DURATION * 2 , Easing . OutQuint ) ;
2024-02-26 21:16:15 +08:00
lowPassFilter ? . CutoffTo ( AudioFilter . MAX_LOWPASS_CUTOFF , CONTENT_OUT_DURATION ) ;
highPassFilter ? . CutoffTo ( 0 , CONTENT_OUT_DURATION ) ;
2020-02-14 17:22:57 +08:00
}
2018-04-13 17:19:50 +08:00
private void pushWhenLoaded ( )
{
2019-01-23 19:52:00 +08:00
if ( ! this . IsCurrentScreen ( ) ) return ;
2018-04-13 17:19:50 +08:00
2020-12-10 15:33:28 +08:00
if ( ! readyForPush )
2018-04-13 17:19:50 +08:00
{
2020-12-10 15:33:28 +08:00
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
// if we become unready for push during the delay.
cancelLoad ( ) ;
return ;
}
2018-04-13 17:19:50 +08:00
2020-12-10 15:33:28 +08:00
// if a push has already been scheduled, no further action is required.
// this value is reset via cancelLoad() to allow a second usage of the same PlayerLoader screen.
if ( scheduledPushPlayer ! = null )
return ;
2018-04-13 17:19:50 +08:00
2024-02-26 21:13:15 +08:00
// Now that everything's been loaded, we can safely switch to a higher performance session without incurring too much overhead.
// Doing this prior to the game being pushed gives us a bit of time to stabilise into the high performance mode before gameplay starts.
highPerformanceSession ? ? = highPerformanceSessionManager ? . BeginSession ( ) ;
2020-12-10 15:33:28 +08:00
scheduledPushPlayer = Scheduler . AddDelayed ( ( ) = >
{
2020-12-10 15:32:14 +08:00
// ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared).
var consumedPlayer = consumePlayer ( ) ;
2018-04-13 17:19:50 +08:00
2021-11-12 19:30:40 +08:00
ContentOut ( ) ;
2020-10-20 06:22:30 +08:00
2022-10-13 16:29:07 +08:00
TransformSequence < PlayerLoader > pushSequence = this . Delay ( 0 ) ;
2020-10-20 05:32:44 +08:00
2020-12-10 15:33:28 +08:00
// only show if the warning was created (i.e. the beatmap needs it)
// and this is not a restart of the map (the warning expires after first load).
2023-03-14 17:32:24 +08:00
//
// note the late check of storyboard enable as the user may have just changed it
// from the settings on the loader screen.
if ( epilepsyWarning ? . IsAlive = = true & & showStoryboards . Value )
2020-12-10 15:33:28 +08:00
{
const double epilepsy_display_length = 3000 ;
pushSequence
2022-10-13 16:29:07 +08:00
. Delay ( CONTENT_OUT_DURATION )
2020-12-10 15:33:28 +08:00
. Schedule ( ( ) = > epilepsyWarning . State . Value = Visibility . Visible )
. TransformBindableTo ( volumeAdjustment , 0.25 , EpilepsyWarning . FADE_DURATION , Easing . OutQuint )
. Delay ( epilepsy_display_length )
. Schedule ( ( ) = >
{
epilepsyWarning . Hide ( ) ;
epilepsyWarning . Expire ( ) ;
} )
. Delay ( EpilepsyWarning . FADE_DURATION ) ;
}
2021-10-13 12:26:20 +08:00
else
{
// This goes hand-in-hand with the restoration of low pass filter in contentOut().
2021-11-12 19:30:40 +08:00
this . TransformBindableTo ( volumeAdjustment , 0 , CONTENT_OUT_DURATION , Easing . OutCubic ) ;
2023-04-06 02:56:50 +08:00
epilepsyWarning ? . Expire ( ) ;
2021-10-13 12:26:20 +08:00
}
2018-04-13 17:19:50 +08:00
2020-12-10 15:33:28 +08:00
pushSequence . Schedule ( ( ) = >
{
if ( ! this . IsCurrentScreen ( ) ) return ;
2018-04-13 17:19:50 +08:00
2020-12-10 15:33:28 +08:00
LoadTask = null ;
// By default, we want to load the player and never be returned to.
// Note that this may change if the player we load requested a re-run.
ValidForResume = false ;
2018-04-20 15:52:15 +08:00
2020-12-10 15:32:14 +08:00
if ( consumedPlayer . LoadedBeatmapSuccessfully )
this . Push ( consumedPlayer ) ;
2020-12-10 15:33:28 +08:00
else
this . Exit ( ) ;
} ) ;
2022-08-06 21:12:36 +08:00
} , 500 ) ;
2018-04-13 17:19:50 +08:00
}
private void cancelLoad ( )
{
2020-02-14 17:22:57 +08:00
scheduledPushPlayer ? . Cancel ( ) ;
scheduledPushPlayer = null ;
2018-04-13 17:19:50 +08:00
}
2020-02-14 17:22:57 +08:00
#region Disposal
2018-04-13 17:19:50 +08:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
2018-05-30 14:47:31 +08:00
if ( isDisposing )
{
// if the player never got pushed, we should explicitly dispose it.
2022-03-17 18:23:43 +08:00
DisposalTask = LoadTask ? . ContinueWith ( _ = > CurrentPlayer ? . Dispose ( ) ) ;
2018-05-30 14:47:31 +08:00
}
2018-04-13 17:19:50 +08:00
}
2020-02-14 17:22:57 +08:00
#endregion
2019-03-20 15:50:47 +08:00
2020-02-14 17:28:58 +08:00
#region Mute warning
2022-03-17 18:36:33 +08:00
private Bindable < bool > muteWarningShownOnce = null ! ;
2020-02-14 17:28:58 +08:00
2021-03-31 12:57:57 +08:00
private int restartCount ;
2024-02-02 15:12:46 +08:00
private const double volume_requirement = 0.01 ;
2021-12-08 12:38:13 +08:00
2020-02-14 17:28:58 +08:00
private void showMuteWarningIfNeeded ( )
{
if ( ! muteWarningShownOnce . Value )
{
2024-02-16 06:18:13 +08:00
double aggregateVolumeTrack = audioManager . Volume . Value * audioManager . VolumeTrack . Value ;
2020-05-05 09:31:11 +08:00
// Checks if the notification has not been shown yet and also if master volume is muted, track/music volume is muted or if the whole game is muted.
2024-02-16 16:13:41 +08:00
if ( volumeOverlay ? . IsMuted . Value = = true | | Precision . AlmostBigger ( volume_requirement , aggregateVolumeTrack ) )
2020-02-14 17:28:58 +08:00
{
notificationOverlay ? . Post ( new MutedNotification ( ) ) ;
muteWarningShownOnce . Value = true ;
}
}
}
2022-11-24 13:32:20 +08:00
private partial class MutedNotification : SimpleNotification
2019-09-15 21:59:46 +08:00
{
2020-02-14 17:22:57 +08:00
public override bool IsImportant = > true ;
2019-09-15 23:47:44 +08:00
public MutedNotification ( )
{
2023-01-14 04:11:25 +08:00
Text = NotificationsStrings . GameVolumeTooLow ;
2019-09-15 23:47:44 +08:00
}
2019-09-15 21:59:46 +08:00
[BackgroundDependencyLoader]
2022-04-18 18:59:57 +08:00
private void load ( OsuColour colours , AudioManager audioManager , INotificationOverlay notificationOverlay , VolumeOverlay volumeOverlay )
2019-09-15 21:59:46 +08:00
{
Icon = FontAwesome . Solid . VolumeMute ;
2022-08-30 16:40:35 +08:00
IconContent . Colour = colours . RedDark ;
2019-09-15 21:59:46 +08:00
Activated = delegate
{
notificationOverlay . Hide ( ) ;
2019-09-15 22:50:01 +08:00
volumeOverlay . IsMuted . Value = false ;
2021-12-08 12:38:13 +08:00
// Check values before resetting, as the user may have only had mute enabled, in which case we might not need to adjust volumes.
2024-02-16 06:31:52 +08:00
// Note that we only restore halfway to ensure the user isn't suddenly overloaded by unexpectedly high volume.
2024-02-16 16:17:54 +08:00
audioManager . Volume . Value = Math . Max ( audioManager . Volume . Value , 0.5 ) ;
audioManager . VolumeTrack . Value = Math . Max ( audioManager . VolumeTrack . Value , 0.5 ) ;
2019-09-15 21:59:46 +08:00
return true ;
} ;
}
}
2020-02-14 17:28:58 +08:00
#endregion
2021-04-08 02:41:21 +08:00
#region Low battery warning
2021-04-09 08:28:23 +08:00
2022-07-30 20:26:19 +08:00
private const double low_battery_threshold = 0.25 ;
2022-03-17 18:36:33 +08:00
private Bindable < bool > batteryWarningShownOnce = null ! ;
2021-04-08 02:41:21 +08:00
private void showBatteryWarningIfNeeded ( )
{
2021-04-12 22:52:12 +08:00
if ( batteryInfo = = null ) return ;
2021-04-10 05:55:41 +08:00
2021-04-08 02:41:21 +08:00
if ( ! batteryWarningShownOnce . Value )
{
2022-07-30 20:26:19 +08:00
if ( batteryInfo . OnBattery & & batteryInfo . ChargeLevel < = low_battery_threshold )
2021-04-08 02:41:21 +08:00
{
notificationOverlay ? . Post ( new BatteryWarningNotification ( ) ) ;
batteryWarningShownOnce . Value = true ;
}
}
}
2022-11-24 13:32:20 +08:00
private partial class BatteryWarningNotification : SimpleNotification
2021-04-08 02:41:21 +08:00
{
public override bool IsImportant = > true ;
public BatteryWarningNotification ( )
{
2023-01-14 07:30:10 +08:00
Text = NotificationsStrings . BatteryLow ;
2021-04-08 02:41:21 +08:00
}
[BackgroundDependencyLoader]
2022-04-18 18:59:57 +08:00
private void load ( OsuColour colours , INotificationOverlay notificationOverlay )
2021-04-08 02:41:21 +08:00
{
Icon = FontAwesome . Solid . BatteryQuarter ;
2022-08-30 16:40:35 +08:00
IconContent . Colour = colours . RedDark ;
2021-04-08 02:41:21 +08:00
Activated = delegate
{
notificationOverlay . Hide ( ) ;
return true ;
} ;
}
}
#endregion
2018-04-13 17:19:50 +08:00
}
}