2019-01-24 08:43:03 +00: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 09:19:50 +00:00
2020-03-23 10:03:42 +00:00
using System ;
2017-08-21 03:31:21 +00:00
using System.Linq ;
2017-08-24 06:23:17 +00:00
using osu.Framework.Allocation ;
2019-02-21 10:04:31 +00:00
using osu.Framework.Bindables ;
2017-12-06 14:03:31 +00:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2017-08-24 06:23:17 +00:00
using osu.Framework.Input ;
2017-08-21 03:31:21 +00:00
using osu.Framework.Input.Bindings ;
2018-10-02 03:44:14 +00:00
using osu.Framework.Input.Events ;
2024-07-03 08:18:45 +00:00
using osu.Framework.Input.StateChanges ;
2018-09-18 08:05:26 +00:00
using osu.Framework.Input.StateChanges.Events ;
2018-07-21 02:38:28 +00:00
using osu.Framework.Input.States ;
2017-08-24 06:23:17 +00:00
using osu.Game.Configuration ;
2021-05-24 08:23:09 +00:00
using osu.Game.Input ;
2017-08-21 03:31:21 +00:00
using osu.Game.Input.Bindings ;
2017-08-24 06:23:17 +00:00
using osu.Game.Input.Handlers ;
2022-01-31 09:54:23 +00:00
using osu.Game.Rulesets.Scoring ;
2023-06-25 13:04:39 +00:00
using osu.Game.Screens.Play.HUD ;
using osu.Game.Screens.Play.HUD.ClicksPerSecond ;
2024-07-03 12:37:07 +00:00
using osuTK ;
2018-06-11 14:00:26 +00:00
using static osu . Game . Input . Handlers . ReplayInputHandler ;
2018-04-13 09:19:50 +00:00
2017-08-21 03:31:21 +00:00
namespace osu.Game.Rulesets.UI
{
2023-06-25 13:04:39 +00:00
public abstract partial class RulesetInputManager < T > : PassThroughInputManager , ICanAttachHUDPieces , IHasReplayHandler , IHasRecordingHandler
2017-08-21 03:31:21 +00:00
where T : struct
{
2023-05-13 12:12:21 +00:00
protected override bool AllowRightClickFromLongTouch = > false ;
2022-01-31 07:47:20 +00:00
public readonly KeyBindingContainer < T > KeyBindingContainer ;
2024-07-03 07:33:48 +00:00
[Resolved]
private ScoreProcessor ? scoreProcessor { get ; set ; }
2022-01-31 09:54:23 +00:00
2024-07-03 07:33:48 +00:00
private ReplayRecorder ? recorder ;
2020-03-23 10:03:42 +00:00
2024-07-03 07:33:48 +00:00
public ReplayRecorder ? Recorder
2020-03-23 10:03:42 +00:00
{
set
{
2023-02-14 07:55:35 +00:00
if ( value = = recorder )
return ;
2021-06-03 16:59:56 +00:00
if ( value ! = null & & recorder ! = null )
2020-03-23 10:03:42 +00:00
throw new InvalidOperationException ( "Cannot attach more than one recorder" ) ;
2021-06-03 16:59:56 +00:00
recorder ? . Expire ( ) ;
2020-03-23 10:03:42 +00:00
recorder = value ;
2021-06-03 16:59:56 +00:00
if ( recorder ! = null )
KeyBindingContainer . Add ( recorder ) ;
2020-03-23 10:03:42 +00:00
}
}
2020-07-01 09:54:11 +00:00
protected override InputState CreateInitialState ( ) = > new RulesetInputManagerInputState < T > ( base . CreateInitialState ( ) ) ;
2018-06-11 14:00:26 +00:00
2019-03-26 02:28:43 +00:00
protected override Container < Drawable > Content = > content ;
private readonly Container content ;
2018-04-13 09:19:50 +00:00
2017-12-06 14:03:31 +00:00
protected RulesetInputManager ( RulesetInfo ruleset , int variant , SimultaneousBindingMode unique )
{
2019-03-26 02:28:43 +00:00
InternalChild = KeyBindingContainer =
2019-03-29 02:36:40 +00:00
CreateKeyBindingContainer ( ruleset , variant , unique )
2019-03-26 02:28:43 +00:00
. WithChild ( content = new Container { RelativeSizeAxes = Axes . Both } ) ;
2019-03-16 04:47:11 +00:00
}
[BackgroundDependencyLoader(true)]
private void load ( OsuConfigManager config )
{
mouseDisabled = config . GetBindable < bool > ( OsuSetting . MouseDisableButtons ) ;
2023-11-06 23:13:46 +00:00
tapsDisabled = config . GetBindable < bool > ( OsuSetting . TouchDisableGameplayTaps ) ;
2017-08-21 03:31:21 +00:00
}
2018-04-13 09:19:50 +00:00
2017-08-24 06:48:40 +00:00
#region Action mapping ( for replays )
2018-04-13 09:19:50 +00:00
2018-09-18 08:05:26 +00:00
public override void HandleInputStateChange ( InputStateChangeEvent inputStateChange )
2017-08-24 06:23:17 +00:00
{
2022-01-31 09:54:23 +00:00
switch ( inputStateChange )
2018-06-11 14:00:26 +00:00
{
2022-01-31 09:54:23 +00:00
case ReplayStateChangeEvent < T > stateChangeEvent :
foreach ( var action in stateChangeEvent . ReleasedActions )
KeyBindingContainer . TriggerReleased ( action ) ;
2018-04-13 09:19:50 +00:00
2022-01-31 09:54:23 +00:00
foreach ( var action in stateChangeEvent . PressedActions )
KeyBindingContainer . TriggerPressed ( action ) ;
break ;
case ReplayStatisticsFrameEvent statisticsStateChangeEvent :
2022-05-31 08:16:23 +00:00
scoreProcessor ? . ResetFromReplayFrame ( statisticsStateChangeEvent . Frame ) ;
2022-01-31 09:54:23 +00:00
break ;
default :
base . HandleInputStateChange ( inputStateChange ) ;
break ;
2018-06-11 14:00:26 +00:00
}
2017-08-24 06:23:17 +00:00
}
2018-04-13 09:19:50 +00:00
2017-08-24 06:48:40 +00:00
#endregion
2018-04-13 09:19:50 +00:00
2017-08-24 06:48:40 +00:00
#region IHasReplayHandler
2018-04-13 09:19:50 +00:00
2024-07-03 07:33:48 +00:00
private ReplayInputHandler ? replayInputHandler ;
2018-10-03 18:03:59 +00:00
2024-07-03 07:33:48 +00:00
public ReplayInputHandler ? ReplayInputHandler
2017-08-24 06:23:17 +00:00
{
2018-10-03 18:03:59 +00:00
get = > replayInputHandler ;
2017-08-24 06:23:17 +00:00
set
{
2024-07-03 08:32:08 +00:00
if ( replayInputHandler = = value )
return ;
2024-07-03 08:18:45 +00:00
if ( replayInputHandler ! = null )
RemoveHandler ( replayInputHandler ) ;
2024-07-03 12:37:07 +00:00
// ensures that all replay keys are released, that the last replay state is correctly cleared,
// and that all user-pressed keys are released, so that the replay handler may trigger them itself
// setting `UseParentInput` will only sync releases (https://github.com/ppy/osu-framework/blob/17d65f476d51cc5f2aaea818534f8fbac47e5fe6/osu.Framework/Input/PassThroughInputManager.cs#L179-L182)
new ReplayStateReset ( ) . Apply ( CurrentState , this ) ;
2018-04-13 09:19:50 +00:00
2017-08-24 06:23:17 +00:00
replayInputHandler = value ;
2018-06-11 14:00:26 +00:00
UseParentInput = replayInputHandler = = null ;
2018-04-13 09:19:50 +00:00
2017-08-24 06:23:17 +00:00
if ( replayInputHandler ! = null )
AddHandler ( replayInputHandler ) ;
}
}
2018-04-13 09:19:50 +00:00
2017-08-24 06:48:40 +00:00
#endregion
2018-04-13 09:19:50 +00:00
2017-08-24 06:48:40 +00:00
#region Setting application ( disables etc . )
2018-04-13 09:19:50 +00:00
2024-07-03 07:33:48 +00:00
private Bindable < bool > mouseDisabled = null ! ;
private Bindable < bool > tapsDisabled = null ! ;
2018-04-13 09:19:50 +00:00
2018-10-03 18:03:59 +00:00
protected override bool Handle ( UIEvent e )
{
2018-10-04 08:55:31 +00:00
switch ( e )
{
2022-06-24 12:25:23 +00:00
case MouseDownEvent :
2018-10-04 08:55:31 +00:00
if ( mouseDisabled . Value )
2021-02-23 05:24:24 +00:00
return true ; // importantly, block upwards propagation so global bindings also don't fire.
2019-02-27 12:07:17 +00:00
2018-10-04 08:55:31 +00:00
break ;
2019-04-01 03:44:46 +00:00
2018-10-04 08:55:31 +00:00
case MouseUpEvent mouseUp :
if ( ! CurrentState . Mouse . IsPressed ( mouseUp . Button ) )
return false ;
2019-02-27 12:07:17 +00:00
2018-10-04 08:55:31 +00:00
break ;
}
2019-02-21 12:24:02 +00:00
2018-10-03 18:03:59 +00:00
return base . Handle ( e ) ;
2022-01-05 09:29:32 +00:00
}
protected override bool HandleMouseTouchStateChange ( TouchStateChangeEvent e )
{
2023-11-06 19:53:22 +00:00
if ( tapsDisabled . Value )
2022-01-05 09:29:32 +00:00
{
2023-11-06 19:53:22 +00:00
// Only propagate positional data when taps are disabled.
2022-01-05 09:29:32 +00:00
e = new TouchStateChangeEvent ( e . State , e . Input , e . Touch , false , e . LastPosition ) ;
}
return base . HandleMouseTouchStateChange ( e ) ;
2018-10-03 18:03:59 +00:00
}
2017-08-24 06:48:40 +00:00
#endregion
2018-04-13 09:19:50 +00:00
2023-06-25 13:04:39 +00:00
#region Key Counter Attachment
2023-06-05 22:04:29 +00:00
2023-06-26 17:27:42 +00:00
public void Attach ( InputCountController inputCountController )
2017-08-21 03:31:21 +00:00
{
2023-06-27 07:35:59 +00:00
var triggers = KeyBindingContainer . DefaultKeyBindings
. Select ( b = > b . GetAction < T > ( ) )
. Distinct ( )
. Select ( action = > new KeyCounterActionTrigger < T > ( action ) )
. ToArray ( ) ;
KeyBindingContainer . AddRange ( triggers ) ;
inputCountController . AddRange ( triggers ) ;
2023-06-25 13:04:39 +00:00
}
2018-04-13 09:19:50 +00:00
2023-06-25 13:04:39 +00:00
#endregion
2022-08-08 19:27:46 +00:00
2023-06-25 13:04:39 +00:00
#region Keys per second Counter Attachment
2023-06-27 07:38:46 +00:00
public void Attach ( ClicksPerSecondController controller ) = > KeyBindingContainer . Add ( new ActionListener ( controller ) ) ;
2022-08-08 19:27:46 +00:00
2022-08-24 15:12:52 +00:00
private partial class ActionListener : Component , IKeyBindingHandler < T >
2022-08-08 19:27:46 +00:00
{
2023-06-27 07:38:46 +00:00
private readonly ClicksPerSecondController controller ;
2022-08-24 15:12:52 +00:00
2023-06-27 07:38:46 +00:00
public ActionListener ( ClicksPerSecondController controller )
2023-06-25 13:04:39 +00:00
{
2023-06-27 07:38:46 +00:00
this . controller = controller ;
2023-06-25 13:04:39 +00:00
}
2022-08-24 10:36:01 +00:00
2022-08-08 19:27:46 +00:00
public bool OnPressed ( KeyBindingPressEvent < T > e )
{
2023-06-27 07:38:46 +00:00
controller . AddInputTimestamp ( ) ;
2022-08-08 19:27:46 +00:00
return false ;
}
public void OnReleased ( KeyBindingReleaseEvent < T > e )
{
}
}
#endregion
2020-03-23 08:33:02 +00:00
protected virtual KeyBindingContainer < T > CreateKeyBindingContainer ( RulesetInfo ruleset , int variant , SimultaneousBindingMode unique )
2018-06-07 11:24:33 +00:00
= > new RulesetKeyBindingContainer ( ruleset , variant , unique ) ;
2019-01-23 05:51:13 +00:00
public partial class RulesetKeyBindingContainer : DatabasedKeyBindingContainer < T >
{
2021-11-18 03:57:51 +00:00
protected override bool HandleRepeats = > false ;
2019-01-23 05:51:13 +00:00
public RulesetKeyBindingContainer ( RulesetInfo ruleset , int variant , SimultaneousBindingMode unique )
: base ( ruleset , variant , unique )
{
}
2021-05-24 08:23:09 +00:00
2022-10-09 14:14:16 +00:00
protected override void ReloadMappings ( IQueryable < RealmKeyBinding > realmKeyBindings )
2021-05-24 08:23:09 +00:00
{
2022-10-09 14:14:16 +00:00
base . ReloadMappings ( realmKeyBindings ) ;
2021-05-24 08:23:09 +00:00
2024-01-29 07:12:00 +00:00
KeyBindings = KeyBindings . Where ( static b = > RealmKeyBindingStore . CheckValidForGameplay ( b . KeyCombination ) ) . ToList ( ) ;
2023-10-16 19:26:46 +00:00
RealmKeyBindingStore . ClearDuplicateBindings ( KeyBindings ) ;
2021-05-24 08:23:09 +00:00
}
2019-01-23 05:51:13 +00:00
}
2024-07-03 08:18:45 +00:00
private class ReplayStateReset : IInput
{
public void Apply ( InputState state , IInputStateChangeHandler handler )
{
if ( ! ( state is RulesetInputManagerInputState < T > inputState ) )
throw new InvalidOperationException ( $"{nameof(ReplayState<T>)} should only be applied to a {nameof(RulesetInputManagerInputState<T>)}" ) ;
2024-07-03 12:37:07 +00:00
new MouseButtonInput ( [ ] , state . Mouse . Buttons ) . Apply ( state , handler ) ;
new KeyboardKeyInput ( [ ] , state . Keyboard . Keys ) . Apply ( state , handler ) ;
new TouchInput ( Enum . GetValues < TouchSource > ( ) . Select ( s = > new Touch ( s , Vector2 . Zero ) ) , false ) . Apply ( state , handler ) ;
new JoystickButtonInput ( [ ] , state . Joystick . Buttons ) . Apply ( state , handler ) ;
new MidiKeyInput ( new MidiState ( ) , state . Midi ) . Apply ( state , handler ) ;
new TabletPenButtonInput ( [ ] , state . Tablet . PenButtons ) . Apply ( state , handler ) ;
new TabletAuxiliaryButtonInput ( [ ] , state . Tablet . AuxiliaryButtons ) . Apply ( state , handler ) ;
2024-07-03 08:18:45 +00:00
handler . HandleInputStateChange ( new ReplayStateChangeEvent < T > ( state , this , inputState . LastReplayState ? . PressedActions . ToArray ( ) ? ? [ ] , [ ] ) ) ;
2024-07-03 12:37:07 +00:00
inputState . LastReplayState = null ;
2024-07-03 08:18:45 +00:00
}
}
2017-08-21 03:31:21 +00:00
}
2018-04-13 09:19:50 +00:00
2018-06-11 14:00:26 +00:00
public class RulesetInputManagerInputState < T > : InputState
2018-10-03 18:03:59 +00:00
where T : struct
2018-06-11 14:00:26 +00:00
{
2024-07-03 07:33:48 +00:00
public ReplayState < T > ? LastReplayState ;
2018-09-19 11:52:57 +00:00
2024-07-03 07:33:48 +00:00
public RulesetInputManagerInputState ( InputState state )
2020-07-01 09:54:11 +00:00
: base ( state )
2018-09-19 11:52:57 +00:00
{
}
2018-06-11 14:00:26 +00:00
}
2017-08-24 11:32:55 +00:00
}