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