2022-06-07 13:40:21 +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.
#nullable enable
using System ;
2022-06-07 17:11:54 +08:00
using System.Diagnostics ;
2022-06-07 19:28:42 +08:00
using System.Linq ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Allocation ;
2022-06-07 19:28:42 +08:00
using osu.Framework.Bindables ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Configuration ;
using osu.Framework.Extensions.Color4Extensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
using osu.Framework.Graphics.Containers ;
2022-06-07 21:52:24 +08:00
using osu.Framework.Graphics.Effects ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Graphics.Shapes ;
2022-06-07 19:02:26 +08:00
using osu.Framework.Graphics.Sprites ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Input ;
using osu.Framework.Input.Events ;
2022-06-07 17:27:42 +08:00
using osu.Framework.Localisation ;
2022-06-07 18:03:26 +08:00
using osu.Framework.Platform ;
using osu.Framework.Platform.Windows ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Screens ;
using osu.Framework.Utils ;
using osu.Game.Graphics ;
using osu.Game.Graphics.Containers ;
2022-06-07 21:52:24 +08:00
using osu.Game.Graphics.Sprites ;
2022-06-07 13:40:21 +08:00
using osu.Game.Overlays ;
using osu.Game.Overlays.Settings ;
using osuTK ;
using osuTK.Input ;
namespace osu.Game.Screens
{
public class LatencyComparerScreen : OsuScreen
{
private FrameSync previousFrameSyncMode ;
2022-06-07 18:03:26 +08:00
private double previousActiveHz ;
2022-06-07 13:40:21 +08:00
2022-06-07 13:51:16 +08:00
private readonly OsuTextFlowContainer statusText ;
2022-06-07 13:40:21 +08:00
public override bool HideOverlaysOnEnter = > true ;
2022-06-07 18:31:56 +08:00
public override bool CursorVisible = > mainArea . Count = = 0 ;
2022-06-07 13:40:21 +08:00
public override float BackgroundParallaxAmount = > 0 ;
2022-06-07 14:21:19 +08:00
private readonly OsuTextFlowContainer explanatoryText ;
2022-06-07 19:28:42 +08:00
private readonly Container < LatencyArea > mainArea ;
2022-06-07 13:51:16 +08:00
private readonly Container resultsArea ;
2022-06-07 13:40:21 +08:00
2022-06-07 18:03:26 +08:00
/// <summary>
/// The rate at which the game host should attempt to run.
/// </summary>
private const int target_host_update_frames = 4000 ;
2022-06-07 13:40:21 +08:00
[Cached]
private readonly OverlayColourProvider overlayColourProvider = new OverlayColourProvider ( OverlayColourScheme . Orange ) ;
2022-06-07 17:11:54 +08:00
[Resolved]
private OsuColour colours { get ; set ; } = null ! ;
2022-06-07 13:40:21 +08:00
[Resolved]
private FrameworkConfigManager config { get ; set ; } = null ! ;
2022-06-07 17:11:54 +08:00
private const int rounds_to_complete = 5 ;
2022-06-07 21:52:24 +08:00
private const int rounds_to_complete_certified = 20 ;
2022-06-07 17:11:54 +08:00
private int round ;
private int correctCount ;
private int targetRoundCount = rounds_to_complete ;
2022-06-07 21:52:24 +08:00
private int difficultyLevel = 1 ;
2022-06-07 17:11:54 +08:00
2022-06-07 18:43:33 +08:00
private double lastPoll ;
private int pollingMax ;
2022-06-07 18:03:26 +08:00
[Resolved]
private GameHost host { get ; set ; } = null ! ;
2022-06-07 13:40:21 +08:00
public LatencyComparerScreen ( )
{
InternalChildren = new Drawable [ ]
{
2022-06-07 13:51:16 +08:00
new Box
{
Colour = overlayColourProvider . Background6 ,
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 19:28:42 +08:00
mainArea = new Container < LatencyArea >
2022-06-07 13:40:21 +08:00
{
RelativeSizeAxes = Axes . Both ,
} ,
// Make sure the edge between the two comparisons can't be used to ascertain latency.
new Box
{
Name = "separator" ,
Colour = ColourInfo . GradientHorizontal ( overlayColourProvider . Background6 , overlayColourProvider . Background6 . Opacity ( 0 ) ) ,
2022-06-07 19:42:19 +08:00
Width = 100 ,
2022-06-07 13:40:21 +08:00
RelativeSizeAxes = Axes . Y ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopLeft ,
} ,
new Box
{
Name = "separator" ,
Colour = ColourInfo . GradientHorizontal ( overlayColourProvider . Background6 . Opacity ( 0 ) , overlayColourProvider . Background6 ) ,
2022-06-07 19:42:19 +08:00
Width = 100 ,
2022-06-07 13:40:21 +08:00
RelativeSizeAxes = Axes . Y ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopRight ,
} ,
2022-06-07 14:21:19 +08:00
explanatoryText = new OsuTextFlowContainer ( cp = > cp . Font = OsuFont . Default . With ( size : 20 ) )
{
2022-06-07 17:11:54 +08:00
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
2022-06-07 14:21:19 +08:00
TextAnchor = Anchor . TopCentre ,
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Text = @ "Welcome to the latency comparer!
2022-06-07 19:03:49 +08:00
Use the arrow keys , Z / X / J / K to move the square .
2022-06-07 14:21:19 +08:00
You can click the targets but you don ' t have to .
2022-06-07 19:28:42 +08:00
Use the Tab key to change focus .
2022-06-07 14:21:19 +08:00
Do whatever you need to try and perceive the difference in latency , then choose your best side .
",
} ,
2022-06-07 21:52:24 +08:00
resultsArea = new Container
{
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 13:51:16 +08:00
statusText = new OsuTextFlowContainer ( cp = > cp . Font = OsuFont . Default . With ( size : 40 ) )
2022-06-07 13:40:21 +08:00
{
2022-06-07 17:11:54 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
2022-06-07 13:51:16 +08:00
TextAnchor = Anchor . TopCentre ,
2022-06-07 19:02:26 +08:00
Y = 150 ,
2022-06-07 13:51:16 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
} ,
2022-06-07 13:40:21 +08:00
} ;
}
2022-06-07 18:43:33 +08:00
protected override bool OnMouseMove ( MouseMoveEvent e )
{
if ( lastPoll > 0 )
pollingMax = ( int ) Math . Max ( pollingMax , 1000 / ( Clock . CurrentTime - lastPoll ) ) ;
lastPoll = Clock . CurrentTime ;
return base . OnMouseMove ( e ) ;
}
2022-06-07 13:40:21 +08:00
public override void OnEntering ( ScreenTransitionEvent e )
{
base . OnEntering ( e ) ;
previousFrameSyncMode = config . Get < FrameSync > ( FrameworkSetting . FrameSync ) ;
2022-06-07 18:03:26 +08:00
previousActiveHz = host . UpdateThread . ActiveHz ;
2022-06-07 13:40:21 +08:00
config . SetValue ( FrameworkSetting . FrameSync , FrameSync . Unlimited ) ;
2022-06-07 18:03:26 +08:00
host . UpdateThread . ActiveHz = target_host_update_frames ;
2022-06-07 13:40:21 +08:00
// host.AllowBenchmarkUnlimitedFrames = true;
}
public override bool OnExiting ( ScreenExitEvent e )
{
// host.AllowBenchmarkUnlimitedFrames = false;
config . SetValue ( FrameworkSetting . FrameSync , previousFrameSyncMode ) ;
2022-06-07 18:03:26 +08:00
host . UpdateThread . ActiveHz = previousActiveHz ;
2022-06-07 13:40:21 +08:00
return base . OnExiting ( e ) ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
loadNextRound ( ) ;
}
2022-06-07 19:28:42 +08:00
protected override bool OnKeyDown ( KeyDownEvent e )
{
switch ( e . Key )
{
case Key . Tab :
var firstArea = mainArea . FirstOrDefault ( a = > ! a . IsActiveArea . Value ) ;
if ( firstArea ! = null )
firstArea . IsActiveArea . Value = true ;
return true ;
}
return base . OnKeyDown ( e ) ;
}
2022-06-07 13:40:21 +08:00
private void recordResult ( bool correct )
{
2022-06-07 14:21:19 +08:00
explanatoryText . FadeOut ( 500 , Easing . OutQuint ) ;
2022-06-07 13:40:21 +08:00
if ( correct )
correctCount + + ;
2022-06-07 13:51:16 +08:00
if ( round < targetRoundCount )
2022-06-07 13:40:21 +08:00
loadNextRound ( ) ;
else
{
showResults ( ) ;
}
}
private void loadNextRound ( )
{
round + + ;
2022-06-07 21:52:24 +08:00
statusText . Text = $"Level {difficultyLevel}\nRound {round} of {targetRoundCount}" ;
2022-06-07 13:40:21 +08:00
2022-06-07 13:51:16 +08:00
mainArea . Clear ( ) ;
2022-06-07 13:40:21 +08:00
int betterSide = RNG . Next ( 0 , 2 ) ;
2022-06-07 21:52:24 +08:00
mainArea . Add ( new LatencyArea ( Key . Number1 , betterSide = = 1 ? mapDifficultyToTargetFrameRate ( difficultyLevel ) : 0 )
2022-06-07 13:40:21 +08:00
{
Width = 0.5f ,
2022-06-07 19:28:42 +08:00
ReportBetter = ( ) = > recordResult ( betterSide = = 0 ) ,
IsActiveArea = { Value = true }
2022-06-07 13:40:21 +08:00
} ) ;
2022-06-07 21:52:24 +08:00
mainArea . Add ( new LatencyArea ( Key . Number2 , betterSide = = 0 ? mapDifficultyToTargetFrameRate ( difficultyLevel ) : 0 )
2022-06-07 13:40:21 +08:00
{
Width = 0.5f ,
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
ReportBetter = ( ) = > recordResult ( betterSide = = 1 )
} ) ;
2022-06-07 19:28:42 +08:00
foreach ( var area in mainArea )
{
area . IsActiveArea . BindValueChanged ( active = >
{
if ( active . NewValue )
mainArea . Children . First ( a = > a ! = area ) . IsActiveArea . Value = false ;
} ) ;
}
2022-06-07 13:40:21 +08:00
}
private void showResults ( )
{
2022-06-07 13:51:16 +08:00
mainArea . Clear ( ) ;
2022-06-07 18:03:26 +08:00
var displayMode = host . Window . CurrentDisplayMode . Value ;
string exclusive = "unknown" ;
if ( host . Window is WindowsWindow windowsWindow )
exclusive = windowsWindow . FullscreenCapability . ToString ( ) ;
statusText . Clear ( ) ;
float successRate = ( float ) correctCount / targetRoundCount ;
2022-06-07 21:52:24 +08:00
bool isPass = successRate = = 1 ;
2022-06-07 18:03:26 +08:00
2022-06-07 19:02:26 +08:00
statusText . AddParagraph ( $"You scored {correctCount} out of {targetRoundCount} ({successRate:0%})!" , cp = > cp . Colour = isPass ? colours . Green : colours . Red ) ;
2022-06-07 21:52:24 +08:00
statusText . AddParagraph ( $"Level {difficultyLevel} ({mapDifficultyToTargetFrameRate(difficultyLevel):N0} hz)" ,
2022-06-07 18:43:33 +08:00
cp = > cp . Font = OsuFont . Default . With ( size : 24 ) ) ;
2022-06-07 18:03:26 +08:00
2022-06-07 19:02:26 +08:00
statusText . AddParagraph ( string . Empty ) ;
statusText . AddParagraph ( string . Empty ) ;
statusText . AddIcon ( isPass ? FontAwesome . Regular . CheckCircle : FontAwesome . Regular . TimesCircle , cp = > cp . Colour = isPass ? colours . Green : colours . Red ) ;
statusText . AddParagraph ( string . Empty ) ;
2022-06-07 21:52:24 +08:00
if ( ! isPass & & difficultyLevel > 1 )
{
statusText . AddParagraph ( "To complete certification, decrease the difficulty level until you can get 20 tests correct in a row!" , cp = > cp . Font = OsuFont . Default . With ( size : 24 ) ) ;
statusText . AddParagraph ( string . Empty ) ;
}
2022-06-07 19:02:26 +08:00
statusText . AddParagraph ( $"Polling: {pollingMax} hz Monitor: {displayMode.RefreshRate:N0} hz Exclusive: {exclusive}" , cp = > cp . Font = OsuFont . Default . With ( size : 15 ) ) ;
statusText . AddParagraph ( $"Input: {host.InputThread.Clock.FramesPerSecond} hz "
+ $"Update: {host.UpdateThread.Clock.FramesPerSecond} hz "
+ $"Draw: {host.DrawThread.Clock.FramesPerSecond} hz"
, cp = > cp . Font = OsuFont . Default . With ( size : 15 ) ) ;
2022-06-07 18:03:26 +08:00
2022-06-07 21:52:24 +08:00
int certificationRemaining = ! isPass ? rounds_to_complete_certified : rounds_to_complete_certified - correctCount ;
if ( isPass & & certificationRemaining < = 0 )
{
Drawable background ;
Drawable certifiedText ;
resultsArea . AddRange ( new [ ]
{
background = new Box
{
Colour = overlayColourProvider . Background4 ,
RelativeSizeAxes = Axes . Both ,
} ,
( certifiedText = new OsuSpriteText
{
Alpha = 0 ,
Font = OsuFont . TorusAlternate . With ( size : 80 , weight : FontWeight . Bold ) ,
Text = "Certified!" ,
Blending = BlendingParameters . Additive ,
} ) . WithEffect ( new GlowEffect
{
Colour = overlayColourProvider . Colour1 ,
} ) . With ( e = >
{
e . Anchor = Anchor . Centre ;
e . Origin = Anchor . Centre ;
} )
} ) ;
background . FadeInFromZero ( 1000 , Easing . OutQuint ) ;
certifiedText . FadeInFromZero ( 500 , Easing . InQuint ) ;
certifiedText
. ScaleTo ( 10 )
. ScaleTo ( 1 , 600 , Easing . InQuad )
. Then ( )
. ScaleTo ( 1.05f , 10000 , Easing . OutQuint ) ;
return ;
}
2022-06-07 18:03:26 +08:00
string cannotIncreaseReason = string . Empty ;
if ( ! isPass )
2022-06-07 21:52:24 +08:00
cannotIncreaseReason = "You didn't get a perfect score." ;
else if ( mapDifficultyToTargetFrameRate ( difficultyLevel + 1 ) > target_host_update_frames )
2022-06-07 18:03:26 +08:00
cannotIncreaseReason = "You've reached the limits of this comparison mode." ;
2022-06-07 21:52:24 +08:00
else if ( mapDifficultyToTargetFrameRate ( difficultyLevel + 1 ) > Clock . FramesPerSecond )
2022-06-07 18:03:26 +08:00
cannotIncreaseReason = "Game is not running fast enough to test this level" ;
2022-06-07 13:51:16 +08:00
2022-06-07 17:11:54 +08:00
resultsArea . Add ( new FillFlowContainer
2022-06-07 13:40:21 +08:00
{
2022-06-07 19:02:26 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
2022-06-07 17:11:54 +08:00
Spacing = new Vector2 ( 20 ) ,
2022-06-07 19:02:26 +08:00
Padding = new MarginPadding ( 20 ) ,
2022-06-07 13:40:21 +08:00
Children = new Drawable [ ]
{
2022-06-07 21:52:24 +08:00
new Button ( Key . Enter )
2022-06-07 17:11:54 +08:00
{
2022-06-07 21:52:24 +08:00
Text = "Continue to next level" ,
2022-06-07 17:11:54 +08:00
BackgroundColour = colours . Red2 ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2022-06-07 21:52:24 +08:00
Action = ( ) = > changeDifficulty ( difficultyLevel + 1 ) ,
2022-06-07 18:03:26 +08:00
Enabled = { Value = string . IsNullOrEmpty ( cannotIncreaseReason ) } ,
TooltipText = cannotIncreaseReason
2022-06-07 17:11:54 +08:00
} ,
2022-06-07 17:27:42 +08:00
new Button ( Key . D )
2022-06-07 17:11:54 +08:00
{
2022-06-07 21:52:24 +08:00
Text = difficultyLevel = = 1 ? "Retry" : "Return to last level" ,
2022-06-07 17:11:54 +08:00
BackgroundColour = colours . Green ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2022-06-07 21:52:24 +08:00
Action = ( ) = > changeDifficulty ( Math . Max ( difficultyLevel - 1 , 1 ) ) ,
} ,
2022-06-07 22:02:15 +08:00
new Button ( Key . C )
2022-06-07 21:52:24 +08:00
{
Text = $"Continue towards certification at this level ({certificationRemaining} more)" ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Action = ( ) = >
{
resultsArea . Clear ( ) ;
targetRoundCount + = rounds_to_complete ;
loadNextRound ( ) ;
} ,
TooltipText = isPass ? $"Chain {rounds_to_complete_certified} to confirm your perception!" : "You've reached your limits. Go to the previous level to complete certification!" ,
Enabled = { Value = isPass } ,
} ,
2022-06-07 13:40:21 +08:00
}
} ) ;
}
2022-06-07 17:11:54 +08:00
private void changeDifficulty ( int diff )
{
Debug . Assert ( diff > 0 ) ;
resultsArea . Clear ( ) ;
correctCount = 0 ;
round = 0 ;
2022-06-07 18:43:33 +08:00
pollingMax = 0 ;
lastPoll = 0 ;
2022-06-07 17:11:54 +08:00
targetRoundCount = rounds_to_complete ;
2022-06-07 21:52:24 +08:00
difficultyLevel = diff ;
2022-06-07 17:11:54 +08:00
loadNextRound ( ) ;
}
2022-06-07 18:03:26 +08:00
private static int mapDifficultyToTargetFrameRate ( int difficulty )
{
switch ( difficulty )
{
case 1 :
return 15 ;
case 2 :
return 30 ;
case 3 :
return 45 ;
case 4 :
return 60 ;
case 5 :
return 120 ;
case 6 :
return 240 ;
case 7 :
return 480 ;
case 8 :
return 720 ;
case 9 :
return 960 ;
default :
return 1000 + ( ( difficulty - 10 ) * 500 ) ;
}
}
2022-06-07 13:40:21 +08:00
public class LatencyArea : CompositeDrawable
{
[Resolved]
private OverlayColourProvider overlayColourProvider { get ; set ; } = null ! ;
public Action ? ReportBetter { get ; set ; }
2022-06-07 14:21:19 +08:00
private Drawable ? background ;
2022-06-07 13:40:21 +08:00
2022-06-07 17:27:42 +08:00
private readonly Key key ;
2022-06-07 22:02:15 +08:00
public readonly int TargetFrameRate ;
2022-06-07 13:40:21 +08:00
2022-06-07 19:28:42 +08:00
public readonly BindableBool IsActiveArea = new BindableBool ( ) ;
2022-06-07 18:31:56 +08:00
public LatencyArea ( Key key , int targetFrameRate )
2022-06-07 13:40:21 +08:00
{
2022-06-07 17:27:42 +08:00
this . key = key ;
2022-06-07 22:02:15 +08:00
TargetFrameRate = targetFrameRate ;
2022-06-07 13:40:21 +08:00
RelativeSizeAxes = Axes . Both ;
Masking = true ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
InternalChildren = new [ ]
{
background = new Box
{
Colour = overlayColourProvider . Background6 ,
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 18:31:56 +08:00
new Button ( key )
{
Text = "Feels better" ,
Y = 20 ,
Width = 0.8f ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
Action = ( ) = > ReportBetter ? . Invoke ( ) ,
} ,
2022-06-07 17:11:54 +08:00
new Container
2022-06-07 13:40:21 +08:00
{
RelativeSizeAxes = Axes . Both ,
2022-06-07 13:51:16 +08:00
Children = new Drawable [ ]
{
2022-06-07 19:28:42 +08:00
new LatencyMovableBox ( IsActiveArea )
2022-06-07 13:51:16 +08:00
{
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 19:28:42 +08:00
new LatencyCursorContainer ( IsActiveArea )
2022-06-07 13:51:16 +08:00
{
RelativeSizeAxes = Axes . Both ,
} ,
}
2022-06-07 13:40:21 +08:00
} ,
} ;
2022-06-07 19:28:42 +08:00
IsActiveArea . BindValueChanged ( active = >
{
background . FadeColour ( active . NewValue ? overlayColourProvider . Background4 : overlayColourProvider . Background6 , 200 , Easing . OutQuint ) ;
} , true ) ;
2022-06-07 13:40:21 +08:00
}
2022-06-07 19:45:37 +08:00
protected override bool OnMouseMove ( MouseMoveEvent e )
2022-06-07 13:40:21 +08:00
{
2022-06-07 19:28:42 +08:00
IsActiveArea . Value = true ;
2022-06-07 19:45:37 +08:00
return base . OnMouseMove ( e ) ;
2022-06-07 13:40:21 +08:00
}
2022-06-07 18:31:56 +08:00
private double lastFrameTime ;
2022-06-07 13:40:21 +08:00
public override bool UpdateSubTree ( )
{
2022-06-07 18:31:56 +08:00
double elapsed = Clock . CurrentTime - lastFrameTime ;
2022-06-07 22:02:15 +08:00
if ( TargetFrameRate > 0 & & elapsed < 1000.0 / TargetFrameRate )
2022-06-07 13:40:21 +08:00
return false ;
2022-06-07 18:31:56 +08:00
lastFrameTime = Clock . CurrentTime ;
2022-06-07 13:40:21 +08:00
return base . UpdateSubTree ( ) ;
}
public class LatencyMovableBox : CompositeDrawable
{
private Box box = null ! ;
private InputManager inputManager = null ! ;
2022-06-07 19:28:42 +08:00
private readonly BindableBool isActive ;
2022-06-07 13:40:21 +08:00
[Resolved]
private OverlayColourProvider overlayColourProvider { get ; set ; } = null ! ;
2022-06-07 19:28:42 +08:00
public LatencyMovableBox ( BindableBool isActive )
{
this . isActive = isActive ;
}
2022-06-07 13:40:21 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
inputManager = GetContainingInputManager ( ) ;
InternalChild = box = new Box
{
Size = new Vector2 ( 40 ) ,
RelativePositionAxes = Axes . Both ,
Position = new Vector2 ( 0.5f ) ,
Origin = Anchor . Centre ,
Colour = overlayColourProvider . Colour1 ,
} ;
}
protected override bool OnHover ( HoverEvent e ) = > false ;
private double? lastFrameTime ;
protected override void Update ( )
{
base . Update ( ) ;
2022-06-07 19:28:42 +08:00
if ( ! isActive . Value )
2022-06-07 13:40:21 +08:00
{
lastFrameTime = null ;
return ;
}
if ( lastFrameTime ! = null )
{
float movementAmount = ( float ) ( Clock . CurrentTime - lastFrameTime ) / 400 ;
2022-06-07 17:11:54 +08:00
var buttons = inputManager . CurrentState . Keyboard . Keys ;
box . Colour = buttons . HasAnyButtonPressed ? overlayColourProvider . Content1 : overlayColourProvider . Colour1 ;
foreach ( var key in buttons )
2022-06-07 13:40:21 +08:00
{
switch ( key )
{
2022-06-07 19:03:49 +08:00
case Key . K :
2022-06-07 13:40:21 +08:00
case Key . Up :
box . Y = MathHelper . Clamp ( box . Y - movementAmount , 0.1f , 0.9f ) ;
break ;
2022-06-07 19:03:49 +08:00
case Key . J :
2022-06-07 13:40:21 +08:00
case Key . Down :
box . Y = MathHelper . Clamp ( box . Y + movementAmount , 0.1f , 0.9f ) ;
break ;
case Key . Z :
case Key . Left :
box . X = MathHelper . Clamp ( box . X - movementAmount , 0.1f , 0.9f ) ;
break ;
case Key . X :
case Key . Right :
box . X = MathHelper . Clamp ( box . X + movementAmount , 0.1f , 0.9f ) ;
break ;
}
}
}
lastFrameTime = Clock . CurrentTime ;
}
}
public class LatencyCursorContainer : CompositeDrawable
{
private Circle cursor = null ! ;
private InputManager inputManager = null ! ;
2022-06-07 19:28:42 +08:00
private readonly BindableBool isActive ;
2022-06-07 13:40:21 +08:00
[Resolved]
private OverlayColourProvider overlayColourProvider { get ; set ; } = null ! ;
2022-06-07 19:28:42 +08:00
public LatencyCursorContainer ( BindableBool isActive )
2022-06-07 13:40:21 +08:00
{
2022-06-07 19:28:42 +08:00
this . isActive = isActive ;
2022-06-07 13:40:21 +08:00
Masking = true ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
InternalChild = cursor = new Circle
{
Size = new Vector2 ( 40 ) ,
Origin = Anchor . Centre ,
Colour = overlayColourProvider . Colour2 ,
} ;
inputManager = GetContainingInputManager ( ) ;
}
protected override bool OnHover ( HoverEvent e ) = > false ;
protected override void Update ( )
{
2022-06-07 13:51:16 +08:00
cursor . Colour = inputManager . CurrentState . Mouse . IsPressed ( MouseButton . Left ) ? overlayColourProvider . Content1 : overlayColourProvider . Colour2 ;
2022-06-07 19:28:42 +08:00
if ( isActive . Value )
2022-06-07 13:40:21 +08:00
{
cursor . Position = ToLocalSpace ( inputManager . CurrentState . Mouse . Position ) ;
cursor . Alpha = 1 ;
}
else
{
cursor . Alpha = 0 ;
}
base . Update ( ) ;
}
}
}
2022-06-07 13:51:16 +08:00
public class Button : SettingsButton
{
2022-06-07 17:27:42 +08:00
private readonly Key key ;
public Button ( Key key )
{
this . key = key ;
}
public override LocalisableString Text
{
get = > base . Text ;
set = > base . Text = $"{value} (Press {key.ToString().Replace(" Number ", string.Empty)})" ;
}
protected override bool OnKeyDown ( KeyDownEvent e )
{
if ( ! e . Repeat & & e . Key = = key )
{
TriggerClick ( ) ;
return true ;
}
return base . OnKeyDown ( e ) ;
}
2022-06-07 13:51:16 +08:00
[Resolved]
private OverlayColourProvider overlayColourProvider { get ; set ; } = null ! ;
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
Height = 100 ;
SpriteText . Colour = overlayColourProvider . Background6 ;
SpriteText . Font = OsuFont . TorusAlternate . With ( size : 34 ) ;
}
}
2022-06-07 13:40:21 +08:00
}
}