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-03-23 14:04:40 +08:00
using System.Threading.Tasks ;
2021-11-10 16:08:51 +08:00
using ManagedBass.Fx ;
2017-02-22 20:43:29 +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 ;
2017-02-22 20:43:29 +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 ;
2017-02-22 20:43:29 +08:00
using osu.Framework.Screens ;
2018-05-09 22:31:52 +08:00
using osu.Framework.Threading ;
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 ;
2017-02-22 20:43:29 +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 ;
2019-09-15 21:59:46 +08:00
using osu.Game.Overlays ;
using osu.Game.Overlays.Notifications ;
2017-11-01 19:54:58 +08:00
using osu.Game.Screens.Menu ;
2018-01-27 04:29:54 +08:00
using osu.Game.Screens.Play.PlayerSettings ;
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
2017-02-22 20:43:29 +08:00
namespace osu.Game.Screens.Play
{
2018-02-16 04:57:53 +08:00
public class PlayerLoader : ScreenWithBeatmapBackground
2017-02-22 20:43:29 +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
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
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 ) ;
2022-03-17 18:36:33 +08:00
private AudioFilter lowPassFilter = null ! ;
private AudioFilter highPassFilter = null ! ;
2021-10-07 13:15:16 +08:00
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
2019-10-03 18:16:31 +08:00
[Resolved(CanBeNull = true)]
2022-04-19 05:18:10 +08:00
private INotificationOverlay ? notificationOverlay { get ; set ; }
2019-10-03 18:16:31 +08:00
[Resolved(CanBeNull = true)]
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
2021-04-10 05:55:41 +08:00
[Resolved(CanBeNull = true)]
2022-03-17 18:36:33 +08:00
private BatteryInfo ? batteryInfo { get ; set ; }
2021-04-09 07:34:35 +08:00
2018-12-26 21:16:35 +08:00
public PlayerLoader ( Func < Player > createPlayer )
2017-02-22 20:43:29 +08:00
{
2018-12-26 21:16:35 +08:00
this . createPlayer = createPlayer ;
}
2018-04-13 17:19:50 +08:00
2017-02-22 20:43:29 +08:00
[BackgroundDependencyLoader]
2021-10-07 13:15:16 +08:00
private void load ( SessionStatics sessionStatics , AudioManager audio )
2017-02-22 20:43:29 +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 ) ;
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-03-03 15:23:30 +08:00
new OsuScrollContainer
2021-10-13 12:26:20 +08:00
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
2022-03-03 15:23:30 +08:00
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-03-03 15:23:30 +08:00
AutoSizeAxes = Axes . Both ,
Direction = FillDirection . Vertical ,
Spacing = new Vector2 ( 0 , 20 ) ,
Padding = new MarginPadding { Horizontal = padding } ,
Children = new PlayerSettingsGroup [ ]
{
VisualSettings = new VisualSettings ( ) ,
AudioSettings = new AudioSettings ( ) ,
new InputSettings ( )
}
} ,
2021-10-13 12:26:20 +08:00
} ,
idleTracker = new IdleTracker ( 750 ) ,
} ) ,
2021-11-10 16:08:51 +08:00
lowPassFilter = new AudioFilter ( audio . TrackMixer ) ,
highPassFilter = new AudioFilter ( audio . TrackMixer , BQFType . HighPass )
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 ,
} ) ;
}
2017-02-22 20:43:29 +08:00
}
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
2019-12-27 18:36:48 +08:00
content . ScaleTo ( 0.7f ) ;
contentIn ( ) ;
2020-01-09 02:55:35 +08:00
MetadataInfo . Delay ( 750 ) . FadeIn ( 500 ) ;
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 )
2017-04-18 12:30:51 +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 ) ;
2022-03-17 18:23:43 +08:00
var lastScore = CurrentPlayer . Score ;
2022-03-01 13:47:06 +08:00
2022-03-01 15:14:57 +08:00
AudioSettings . ReferenceScore . Value = lastScore ? . ScoreInfo ;
2022-03-01 13:47:06 +08:00
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
2017-04-18 16:00:58 +08:00
contentIn ( ) ;
2017-04-18 12:30:51 +08:00
}
2018-04-13 17:19:50 +08:00
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 ) ;
2021-10-07 19:52:36 +08:00
lowPassFilter . CutoffTo ( AudioFilter . MAX_LOWPASS_CUTOFF ) ;
2021-11-10 16:08:51 +08:00
highPassFilter . CutoffTo ( 0 ) ;
2018-12-26 21:16:35 +08:00
}
2022-04-21 23:52:44 +08:00
public override bool OnExiting ( ScreenExitEvent e )
2017-04-18 16:00:58 +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
2022-04-21 23:52:44 +08:00
return base . OnExiting ( e ) ;
2017-04-18 16:00:58 +08:00
}
2018-04-13 17:19:50 +08:00
2017-11-09 16:38:20 +08:00
protected override void LogoArriving ( OsuLogo logo , bool resuming )
2017-11-01 19:54:58 +08:00
{
2017-11-09 16:38:20 +08:00
base . LogoArriving ( logo , resuming ) ;
2018-04-13 17:19:50 +08:00
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 ) ;
2017-11-02 20:52:01 +08:00
logo . FadeIn ( 350 ) ;
2018-04-13 17:19:50 +08:00
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 ( ) ;
2017-11-01 19:54:58 +08:00
}
2018-04-13 17:19:50 +08:00
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 ( ) ;
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
}
private void restartRequested ( )
{
hideOverlays = true ;
ValidForResume = true ;
}
private void contentIn ( )
{
2020-08-13 11:04:32 +08:00
MetadataInfo . Loading = true ;
2020-02-14 17:22:57 +08:00
content . FadeInFromZero ( 400 ) ;
2020-08-13 11:04:32 +08:00
content . ScaleTo ( 1 , 650 , Easing . OutQuint ) . Then ( ) . Schedule ( prepareNewPlayer ) ;
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
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 ) ;
content . FadeOut ( CONTENT_OUT_DURATION , Easing . OutQuint ) ;
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
2017-04-18 16:07:02 +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 )
2017-09-15 07:15:23 +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
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
2021-11-12 19:30:40 +08:00
TransformSequence < PlayerLoader > pushSequence = this . Delay ( CONTENT_OUT_DURATION ) ;
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).
if ( epilepsyWarning ? . IsAlive = = true )
{
const double epilepsy_display_length = 3000 ;
pushSequence
. 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 ) ;
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 ( ) ;
} ) ;
} , 500 ) ;
2017-02-22 20:43:29 +08:00
}
2018-04-13 17:19:50 +08:00
2018-03-22 12:01:40 +08:00
private void cancelLoad ( )
{
2020-02-14 17:22:57 +08:00
scheduledPushPlayer ? . Cancel ( ) ;
scheduledPushPlayer = null ;
2018-03-22 12:01:40 +08:00
}
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
2018-03-23 14:04:40 +08:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
2018-04-13 17:19:50 +08:00
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-03-23 14:04:40 +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 ;
2021-12-08 12:38:13 +08:00
private const double volume_requirement = 0.05 ;
2020-02-14 17:28:58 +08:00
private void showMuteWarningIfNeeded ( )
{
if ( ! muteWarningShownOnce . 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.
2021-12-08 12:38:13 +08:00
if ( volumeOverlay ? . IsMuted . Value = = true | | audioManager . Volume . Value < = volume_requirement | | audioManager . VolumeTrack . Value < = volume_requirement )
2020-02-14 17:28:58 +08:00
{
notificationOverlay ? . Post ( new MutedNotification ( ) ) ;
muteWarningShownOnce . Value = true ;
}
}
}
2019-09-15 21:59:46 +08:00
private class MutedNotification : SimpleNotification
{
2020-02-14 17:22:57 +08:00
public override bool IsImportant = > true ;
2019-09-15 23:47:44 +08:00
public MutedNotification ( )
{
2021-12-08 12:38:13 +08:00
Text = "Your game volume is too low to hear anything! Click here to restore it." ;
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 ;
2021-12-10 13:04:31 +08:00
IconBackground . 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.
2021-12-09 17:58:47 +08:00
if ( audioManager . Volume . Value < = volume_requirement )
2021-12-08 12:38:13 +08:00
audioManager . Volume . SetDefault ( ) ;
2021-12-09 17:58:47 +08:00
if ( audioManager . VolumeTrack . Value < = volume_requirement )
2021-12-08 12:38:13 +08:00
audioManager . VolumeTrack . SetDefault ( ) ;
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-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 )
{
2021-04-12 23:11:22 +08:00
if ( ! batteryInfo . IsCharging & & batteryInfo . ChargeLevel < = 0.25 )
2021-04-08 02:41:21 +08:00
{
notificationOverlay ? . Post ( new BatteryWarningNotification ( ) ) ;
batteryWarningShownOnce . Value = true ;
}
}
}
private class BatteryWarningNotification : SimpleNotification
{
public override bool IsImportant = > true ;
public BatteryWarningNotification ( )
{
2021-04-10 05:55:41 +08:00
Text = "Your battery level is low! Charge your device to prevent interruptions during gameplay." ;
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 ;
2021-12-10 13:04:31 +08:00
IconBackground . Colour = colours . RedDark ;
2021-04-08 02:41:21 +08:00
Activated = delegate
{
notificationOverlay . Hide ( ) ;
return true ;
} ;
}
}
#endregion
2017-02-22 20:43:29 +08:00
}
}