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-03-06 19:30:14 +08:00
using System ;
2019-04-08 17:32:05 +08:00
using System.Collections.Generic ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2019-12-12 14:05:29 +08:00
using osu.Framework.Extensions.IEnumerableExtensions ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2018-10-02 11:02:47 +08:00
using osu.Framework.Input.Events ;
2018-04-13 17:19:50 +08:00
using osu.Game.Configuration ;
using osu.Game.Graphics.UserInterface ;
using osu.Game.Overlays ;
using osu.Game.Overlays.Notifications ;
2019-04-08 17:32:05 +08:00
using osu.Game.Rulesets.Mods ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets.Scoring ;
using osu.Game.Rulesets.UI ;
using osu.Game.Screens.Play.HUD ;
2018-11-20 15:51:59 +08:00
using osuTK ;
using osuTK.Input ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Screens.Play
{
public class HUDOverlay : Container
{
2019-12-12 15:09:50 +08:00
private const int fade_duration = 400 ;
private const Easing fade_easing = Easing . Out ;
2018-04-13 17:19:50 +08:00
2019-03-26 10:28:43 +08:00
public readonly KeyCounterDisplay KeyCounter ;
2018-04-13 17:19:50 +08:00
public readonly RollingCounter < int > ComboCounter ;
public readonly ScoreCounter ScoreCounter ;
public readonly RollingCounter < double > AccuracyCounter ;
public readonly HealthDisplay HealthDisplay ;
public readonly SongProgress Progress ;
public readonly ModDisplay ModDisplay ;
2019-08-30 17:46:42 +08:00
public readonly HitErrorDisplay HitErrorDisplay ;
2018-11-07 00:47:02 +08:00
public readonly HoldForMenuButton HoldToQuit ;
2018-04-13 17:19:50 +08:00
public readonly PlayerSettingsOverlay PlayerSettingsOverlay ;
2019-07-07 04:30:53 +08:00
public Bindable < bool > ShowHealthbar = new Bindable < bool > ( true ) ;
2019-05-09 17:06:11 +08:00
private readonly ScoreProcessor scoreProcessor ;
private readonly DrawableRuleset drawableRuleset ;
private readonly IReadOnlyList < Mod > mods ;
2019-12-12 14:05:29 +08:00
/// <summary>
/// Whether the elements that can optionally be hidden should be visible.
/// </summary>
public Bindable < bool > ShowHud { get ; } = new BindableBool ( ) ;
private Bindable < bool > configShowHud ;
2019-01-17 15:00:11 +08:00
private readonly Container visibilityContainer ;
2019-12-12 14:05:29 +08:00
2018-04-13 17:19:50 +08:00
private readonly BindableBool replayLoaded = new BindableBool ( ) ;
private static bool hasShownNotificationOnce ;
2019-03-06 19:30:14 +08:00
public Action < double > RequestSeek ;
2019-07-07 04:30:53 +08:00
private readonly Container topScoreContainer ;
2019-12-12 14:05:29 +08:00
private IEnumerable < Drawable > hideTargets = > new Drawable [ ] { visibilityContainer , KeyCounter } ;
2019-04-10 16:13:12 +08:00
public HUDOverlay ( ScoreProcessor scoreProcessor , DrawableRuleset drawableRuleset , IReadOnlyList < Mod > mods )
2018-04-13 17:19:50 +08:00
{
2019-05-09 17:06:11 +08:00
this . scoreProcessor = scoreProcessor ;
this . drawableRuleset = drawableRuleset ;
this . mods = mods ;
2018-04-13 17:19:50 +08:00
RelativeSizeAxes = Axes . Both ;
2019-01-17 15:00:11 +08:00
Children = new Drawable [ ]
2018-04-13 17:19:50 +08:00
{
2019-01-23 13:51:13 +08:00
visibilityContainer = new Container
{
2019-01-17 15:00:11 +08:00
RelativeSizeAxes = Axes . Both ,
2019-01-23 13:51:13 +08:00
Children = new Drawable [ ]
{
2019-07-07 04:30:53 +08:00
topScoreContainer = new Container
2019-01-23 13:51:13 +08:00
{
2019-01-21 16:03:06 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
AutoSizeAxes = Axes . Both ,
2019-01-23 13:51:13 +08:00
Children = new Drawable [ ]
{
2019-01-21 16:03:06 +08:00
AccuracyCounter = CreateAccuracyCounter ( ) ,
ScoreCounter = CreateScoreCounter ( ) ,
ComboCounter = CreateComboCounter ( ) ,
} ,
} ,
2019-07-03 12:27:24 +08:00
HealthDisplay = CreateHealthDisplay ( ) ,
2019-01-17 15:00:11 +08:00
Progress = CreateProgress ( ) ,
ModDisplay = CreateModsContainer ( ) ,
2019-08-30 14:32:47 +08:00
HitErrorDisplay = CreateHitErrorDisplayOverlay ( ) ,
2019-12-12 13:19:04 +08:00
PlayerSettingsOverlay = CreatePlayerSettingsOverlay ( ) ,
2019-01-17 15:00:11 +08:00
}
} ,
new FillFlowContainer
2018-04-13 17:19:50 +08:00
{
2019-01-17 15:00:11 +08:00
Anchor = Anchor . BottomRight ,
Origin = Anchor . BottomRight ,
Position = - new Vector2 ( 5 , TwoLayerButton . SIZE_RETRACTED . Y ) ,
AutoSizeAxes = Axes . Both ,
2019-12-12 15:09:50 +08:00
AutoSizeDuration = fade_duration ,
AutoSizeEasing = fade_easing ,
2019-01-17 15:00:11 +08:00
Direction = FillDirection . Vertical ,
Children = new Drawable [ ]
2018-05-22 15:45:42 +08:00
{
2019-03-05 12:26:54 +08:00
KeyCounter = CreateKeyCounter ( ) ,
2019-01-17 15:00:11 +08:00
HoldToQuit = CreateHoldForMenuButton ( ) ,
2018-05-22 15:45:42 +08:00
}
2018-04-13 17:19:50 +08:00
}
2019-01-17 15:00:11 +08:00
} ;
2019-05-09 17:06:11 +08:00
}
2018-04-13 17:19:50 +08:00
2019-05-09 17:06:11 +08:00
[BackgroundDependencyLoader(true)]
private void load ( OsuConfigManager config , NotificationOverlay notificationOverlay )
{
2019-12-12 15:09:42 +08:00
if ( scoreProcessor ! = null )
BindProcessor ( scoreProcessor ) ;
2018-04-13 17:19:50 +08:00
2019-12-12 15:09:42 +08:00
if ( drawableRuleset ! = null )
{
BindDrawableRuleset ( drawableRuleset ) ;
Progress . Objects = drawableRuleset . Objects ;
Progress . AllowSeeking = drawableRuleset . HasReplayLoaded . Value ;
Progress . RequestSeek = time = > RequestSeek ( time ) ;
Progress . ReferenceClock = drawableRuleset . FrameStableClock ;
}
2018-04-13 17:19:50 +08:00
2019-04-08 17:32:05 +08:00
ModDisplay . Current . Value = mods ;
2018-04-13 17:19:50 +08:00
2019-12-12 14:05:29 +08:00
configShowHud = config . GetBindable < bool > ( OsuSetting . ShowInterface ) ;
if ( ! configShowHud . Value & & ! hasShownNotificationOnce )
{
hasShownNotificationOnce = true ;
notificationOverlay ? . Post ( new SimpleNotification
{
Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab."
} ) ;
}
// start all elements hidden
hideTargets . ForEach ( d = > d . Hide ( ) ) ;
}
public override void Hide ( ) = > throw new InvalidOperationException ( $"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead." ) ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2019-12-12 15:09:50 +08:00
ShowHud . BindValueChanged ( visible = > hideTargets . ForEach ( d = > d . FadeTo ( visible . NewValue ? 1 : 0 , fade_duration , fade_easing ) ) ) ;
2019-07-07 04:30:53 +08:00
ShowHealthbar . BindValueChanged ( healthBar = >
{
if ( healthBar . NewValue )
{
2019-12-12 15:09:50 +08:00
HealthDisplay . FadeIn ( fade_duration , fade_easing ) ;
topScoreContainer . MoveToY ( 30 , fade_duration , fade_easing ) ;
2019-07-07 04:30:53 +08:00
}
else
{
2019-12-12 15:09:50 +08:00
HealthDisplay . FadeOut ( fade_duration , fade_easing ) ;
topScoreContainer . MoveToY ( 0 , fade_duration , fade_easing ) ;
2019-07-07 04:30:53 +08:00
}
} , true ) ;
2018-04-13 17:19:50 +08:00
2019-12-12 14:05:29 +08:00
configShowHud . BindValueChanged ( visible = >
2018-04-13 17:19:50 +08:00
{
2019-12-12 14:05:29 +08:00
if ( ! ShowHud . Disabled )
ShowHud . Value = visible . NewValue ;
} , true ) ;
2018-04-13 17:19:50 +08:00
2019-05-10 14:39:25 +08:00
replayLoaded . BindValueChanged ( replayLoadedValueChanged , true ) ;
2018-04-13 17:19:50 +08:00
}
2019-02-21 17:56:34 +08:00
private void replayLoadedValueChanged ( ValueChangedEvent < bool > e )
2018-04-13 17:19:50 +08:00
{
2019-02-21 17:56:34 +08:00
PlayerSettingsOverlay . ReplayLoaded = e . NewValue ;
2018-04-13 17:19:50 +08:00
2019-02-21 17:56:34 +08:00
if ( e . NewValue )
2018-04-13 17:19:50 +08:00
{
PlayerSettingsOverlay . Show ( ) ;
ModDisplay . FadeIn ( 200 ) ;
2018-10-30 20:32:12 +08:00
KeyCounter . Margin = new MarginPadding ( 10 ) { Bottom = 30 } ;
2018-04-13 17:19:50 +08:00
}
else
{
PlayerSettingsOverlay . Hide ( ) ;
ModDisplay . Delay ( 2000 ) . FadeOut ( 200 ) ;
2018-10-30 20:32:12 +08:00
KeyCounter . Margin = new MarginPadding ( 10 ) ;
2018-04-13 17:19:50 +08:00
}
}
2019-03-19 22:44:15 +08:00
protected virtual void BindDrawableRuleset ( DrawableRuleset drawableRuleset )
2018-04-13 17:19:50 +08:00
{
2019-03-19 22:44:15 +08:00
( drawableRuleset as ICanAttachKeyCounter ) ? . Attach ( KeyCounter ) ;
2018-04-13 17:19:50 +08:00
2019-03-19 22:44:15 +08:00
replayLoaded . BindTo ( drawableRuleset . HasReplayLoaded ) ;
2018-04-13 17:19:50 +08:00
2019-03-20 13:49:33 +08:00
Progress . BindDrawableRuleset ( drawableRuleset ) ;
2018-04-13 17:19:50 +08:00
}
2018-10-02 11:02:47 +08:00
protected override bool OnKeyDown ( KeyDownEvent e )
2018-04-13 17:19:50 +08:00
{
2018-10-02 11:02:47 +08:00
if ( e . Repeat ) return false ;
2018-04-13 17:19:50 +08:00
2018-10-02 11:44:14 +08:00
if ( e . ShiftPressed )
2018-04-13 17:19:50 +08:00
{
2018-10-02 11:02:47 +08:00
switch ( e . Key )
2018-04-13 17:19:50 +08:00
{
case Key . Tab :
2019-12-12 14:05:29 +08:00
configShowHud . Value = ! configShowHud . Value ;
2018-04-13 17:19:50 +08:00
return true ;
}
}
2018-10-02 11:02:47 +08:00
return base . OnKeyDown ( e ) ;
2018-04-13 17:19:50 +08:00
}
protected virtual RollingCounter < double > CreateAccuracyCounter ( ) = > new PercentageCounter
{
TextSize = 20 ,
2019-01-21 16:03:06 +08:00
BypassAutoSizeAxes = Axes . X ,
Anchor = Anchor . TopLeft ,
Origin = Anchor . TopRight ,
Margin = new MarginPadding { Top = 5 , Right = 20 } ,
2018-04-13 17:19:50 +08:00
} ;
2019-01-21 16:03:06 +08:00
protected virtual ScoreCounter CreateScoreCounter ( ) = > new ScoreCounter ( 6 )
2018-04-13 17:19:50 +08:00
{
2019-01-21 16:03:06 +08:00
TextSize = 40 ,
2018-04-13 17:19:50 +08:00
Anchor = Anchor . TopCentre ,
2019-01-21 16:03:06 +08:00
Origin = Anchor . TopCentre ,
} ;
protected virtual RollingCounter < int > CreateComboCounter ( ) = > new SimpleComboCounter
{
2018-04-13 17:19:50 +08:00
TextSize = 20 ,
2019-01-21 16:03:06 +08:00
BypassAutoSizeAxes = Axes . X ,
Anchor = Anchor . TopRight ,
Origin = Anchor . TopLeft ,
Margin = new MarginPadding { Top = 5 , Left = 20 } ,
2018-04-13 17:19:50 +08:00
} ;
protected virtual HealthDisplay CreateHealthDisplay ( ) = > new StandardHealthDisplay
{
Size = new Vector2 ( 1 , 5 ) ,
RelativeSizeAxes = Axes . X ,
Margin = new MarginPadding { Top = 20 }
} ;
2019-03-26 10:28:43 +08:00
protected virtual KeyCounterDisplay CreateKeyCounter ( ) = > new KeyCounterDisplay
2018-04-13 17:19:50 +08:00
{
Anchor = Anchor . BottomRight ,
Origin = Anchor . BottomRight ,
Margin = new MarginPadding ( 10 ) ,
} ;
protected virtual SongProgress CreateProgress ( ) = > new SongProgress
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
RelativeSizeAxes = Axes . X ,
} ;
2018-11-07 00:47:02 +08:00
protected virtual HoldForMenuButton CreateHoldForMenuButton ( ) = > new HoldForMenuButton
2018-04-21 23:24:31 +08:00
{
Anchor = Anchor . BottomRight ,
Origin = Anchor . BottomRight ,
} ;
2018-04-13 17:19:50 +08:00
protected virtual ModDisplay CreateModsContainer ( ) = > new ModDisplay
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
AutoSizeAxes = Axes . Both ,
Margin = new MarginPadding { Top = 20 , Right = 10 } ,
} ;
2019-12-12 15:09:42 +08:00
protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay ( ) = > new HitErrorDisplay ( scoreProcessor , drawableRuleset ? . FirstAvailableHitWindows ) ;
2019-08-20 13:45:51 +08:00
2018-04-13 17:19:50 +08:00
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay ( ) = > new PlayerSettingsOverlay ( ) ;
protected virtual void BindProcessor ( ScoreProcessor processor )
{
ScoreCounter ? . Current . BindTo ( processor . TotalScore ) ;
AccuracyCounter ? . Current . BindTo ( processor . Accuracy ) ;
ComboCounter ? . Current . BindTo ( processor . Combo ) ;
HealthDisplay ? . Current . BindTo ( processor . Health ) ;
2019-02-28 13:35:00 +08:00
if ( HealthDisplay is StandardHealthDisplay shd )
2018-04-13 17:19:50 +08:00
processor . NewJudgement + = shd . Flash ;
}
}
}