using System; using System.Collections.Generic; using BriarQueen.Framework.Coordinators.Events; using BriarQueen.Framework.Events.Gameplay; using BriarQueen.Framework.Events.Input; using BriarQueen.Framework.Events.UI; using BriarQueen.Framework.Managers.UI; using BriarQueen.Framework.Managers.UI.Base; using BriarQueen.Framework.Services.Game; using UnityEngine; using UnityEngine.InputSystem; using VContainer; namespace BriarQueen.Framework.Managers.Input { public enum DeviceInputType { KeyboardAndMouse, XboxController, PlaystationController, SwitchProController } public class InputManager : MonoBehaviour, IDisposable, IManager { [Header("Input")] [SerializeField] private PlayerInput _playerInput; [Header("Virtual Cursor")] [SerializeField] private float _controllerCursorSpeed = 1200f; [SerializeField] private bool _clampCursorToScreen = true; private readonly Dictionary _cachedByName = new(); private InputAction _clickAction; private InputAction _codexAction; private bool _codexShown; private bool _toolScreenShown; private GameService _gameService; private EventCoordinator _eventCoordinator; private InputAction _hideHudAction; private bool _hudHidden; private bool _initialized; private bool _isPaused; private bool _isAnyUIOpen; private InputAction _pauseAction; private InputAction _pointAction; private InputAction _rightClickAction; private InputAction _openToolsAction; private InputAction _nextToolAction; private InputAction _previousToolAction; private InputAction _nextItemAction; private InputAction _previousItemAction; private InputAction _virtualMouseAction; private InputAction _submitAction; private UICursorService _uiCursorService; private Vector2 _controllerCursorInput; public Vector2 PointerPosition { get; private set; } public DeviceInputType DeviceInputType { get; private set; } public string ActiveActionMap => _playerInput != null && _playerInput.currentActionMap != null ? _playerInput.currentActionMap.name : string.Empty; public bool Initialized => _initialized; public bool IsPaused => _isPaused; public bool UsingControllerCursor => DeviceInputType != DeviceInputType.KeyboardAndMouse; public string CurrentControlScheme => _playerInput?.currentControlScheme ?? string.Empty; private void Awake() { PointerPosition = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); } private void Update() { if (!_initialized) return; UpdateControllerCursor(); if (_uiCursorService != null && UsingControllerCursor) _uiCursorService.SetVirtualCursorPosition(PointerPosition); } private void OnDestroy() { Dispose(); } public void Dispose() { if (!_initialized) return; if (_eventCoordinator != null) { _eventCoordinator.Unsubscribe(OnHudStateChanged); _eventCoordinator.Unsubscribe(OnWindowStateChanged); _eventCoordinator.Unsubscribe(OnToolScreenStateChanged); _eventCoordinator.Unsubscribe(OnUIStackChanged); } UnbindCoreInputs(); _initialized = false; } [Inject] public void Construct( EventCoordinator eventCoordinator, UICursorService uiCursorService, GameService gameService) { _eventCoordinator = eventCoordinator; _uiCursorService = uiCursorService; _gameService = gameService; } public void Initialize() { Debug.Log("[InputManager] Initialize called"); if (_initialized) { Debug.Log("[InputManager] Already initialized"); return; } if (_eventCoordinator == null) { Debug.LogWarning("[InputManager] EventCoordinator is null"); return; } if (_playerInput == null) { Debug.LogWarning("[InputManager] PlayerInput is null"); return; } CacheActions(); BindCoreInputs(); DeviceInputType = GetDeviceInputType(_playerInput); ApplyCursorModeForCurrentScheme(); _eventCoordinator.Subscribe(OnHudStateChanged); _eventCoordinator.Subscribe(OnWindowStateChanged); _eventCoordinator.Subscribe(OnToolScreenStateChanged); _eventCoordinator.Subscribe(OnUIStackChanged); _initialized = true; } private void CacheActions() { _cachedByName.Clear(); _pointAction = CacheAction("Point"); _virtualMouseAction = CacheAction("Virtual_Mouse"); _pauseAction = CacheAction("Pause"); _clickAction = CacheAction("Click"); _rightClickAction = CacheAction("Right_Click"); _hideHudAction = CacheAction("Hide_HUD"); _codexAction = CacheAction("Codex"); _openToolsAction = CacheAction("Show_Tools"); _nextToolAction = CacheAction("Next_Tool"); _previousToolAction = CacheAction("Previous_Tool"); _nextItemAction = CacheAction("Next_Item"); _previousItemAction = CacheAction("Previous_Item"); _submitAction = CacheAction("Submit"); } private InputAction CacheAction(string actionName) { var action = GetAction(actionName); if (action != null && !string.IsNullOrWhiteSpace(action.name)) _cachedByName[action.name] = action; return action; } private void BindCoreInputs() { if (_pointAction != null) { _pointAction.performed += OnPoint; _pointAction.canceled += OnPoint; } else { } if (_virtualMouseAction != null) { _virtualMouseAction.performed += OnVirtualMouse; _virtualMouseAction.canceled += OnVirtualMouse; } if (_pauseAction != null) _pauseAction.performed += OnPause; if (_clickAction != null) _clickAction.performed += OnClick; if (_rightClickAction != null) _rightClickAction.performed += OnRightClick; if (_hideHudAction != null) _hideHudAction.performed += OnHideHUD; if (_codexAction != null) _codexAction.performed += OnCodex; if (_openToolsAction != null) _openToolsAction.performed += OnOpenTools; if (_nextToolAction != null) _nextToolAction.performed += OnNextToolClicked; if (_previousToolAction != null) _previousToolAction.performed += OnPreviousToolClicked; if (_nextItemAction != null) _nextItemAction.performed += OnNextItemClicked; if (_previousItemAction != null) _previousItemAction.performed += OnPreviousItemClicked; if (_playerInput != null) _playerInput.onControlsChanged += OnControlsChanged; } private void UnbindCoreInputs() { if (_pointAction != null) { _pointAction.performed -= OnPoint; _pointAction.canceled -= OnPoint; } if (_virtualMouseAction != null) { _virtualMouseAction.performed -= OnVirtualMouse; _virtualMouseAction.canceled -= OnVirtualMouse; } if (_pauseAction != null) _pauseAction.performed -= OnPause; if (_clickAction != null) _clickAction.performed -= OnClick; if (_rightClickAction != null) _rightClickAction.performed -= OnRightClick; if (_hideHudAction != null) _hideHudAction.performed -= OnHideHUD; if (_codexAction != null) _codexAction.performed -= OnCodex; if (_openToolsAction != null) _openToolsAction.performed -= OnOpenTools; if (_nextToolAction != null) _nextToolAction.performed -= OnNextToolClicked; if (_previousToolAction != null) _previousToolAction.performed -= OnPreviousToolClicked; if (_nextItemAction != null) _nextItemAction.performed -= OnNextItemClicked; if (_previousItemAction != null) _previousItemAction.performed -= OnPreviousItemClicked; if (_playerInput != null) _playerInput.onControlsChanged -= OnControlsChanged; } private void UpdateControllerCursor() { if (!UsingControllerCursor) return; if (_controllerCursorInput.sqrMagnitude <= 0.0001f) return; PointerPosition += _controllerCursorInput * (_controllerCursorSpeed * Time.unscaledDeltaTime); if (_clampCursorToScreen) { PointerPosition = new Vector2( Mathf.Clamp(PointerPosition.x, 0f, Screen.width), Mathf.Clamp(PointerPosition.y, 0f, Screen.height) ); } } private void OnControlsChanged(PlayerInput playerInput) { DeviceInputType = GetDeviceInputType(playerInput); ApplyCursorModeForCurrentScheme(); } private void ApplyCursorModeForCurrentScheme() { if (_uiCursorService == null) return; var useVirtualCursor = UsingControllerCursor; _uiCursorService.SetUseVirtualCursor(useVirtualCursor); if (useVirtualCursor) _uiCursorService.SetVirtualCursorPosition(PointerPosition); } public void BindPauseForSkip(Action callback) { if (_pauseAction == null || callback == null) return; _pauseAction.performed -= OnPause; _pauseAction.performed += callback; } public void ResetPauseBind(Action callback) { if (_pauseAction == null || callback == null) return; _pauseAction.performed -= callback; _pauseAction.performed -= OnPause; _pauseAction.performed += OnPause; } public void BindSubmitForStart(Action callback) { if (_submitAction == null || callback == null) return; _submitAction.performed += callback; } public void ResetSubmitBind(Action callback) { if (_submitAction == null || callback == null) return; _submitAction.performed -= callback; } public void BindAction(string actionName, Action callback) { if (callback == null) return; var action = GetCachedAction(actionName); action.performed -= callback; action.performed += callback; } public void UnbindAction(string actionName, Action callback) { if (callback == null) return; var action = GetCachedAction(actionName); action.performed -= callback; } private InputAction GetCachedAction(string actionName) { if (string.IsNullOrWhiteSpace(actionName)) return null; if (_cachedByName.TryGetValue(actionName, out var cached)) return cached; var action = GetAction(actionName); if (action != null) _cachedByName[actionName] = action; return action; } private static DeviceInputType GetDeviceInputType(PlayerInput input) { if (input == null) return DeviceInputType.KeyboardAndMouse; return input.currentControlScheme switch { "Keyboard&Mouse" => DeviceInputType.KeyboardAndMouse, "Xbox" => DeviceInputType.XboxController, "Playstation" => DeviceInputType.PlaystationController, "Switch Pro" => DeviceInputType.SwitchProController, _ => DeviceInputType.KeyboardAndMouse }; } private void OnHudStateChanged(UIToggleHudEvent evt) { _hudHidden = !evt.Show; } private void OnWindowStateChanged(UIWindowStateChangedEvent evt) { if (evt.WindowType == WindowType.CodexWindow) _codexShown = evt.IsOpen; } private void OnToolScreenStateChanged(ToggleToolScreenEvent evt) { _toolScreenShown = evt.Shown; } private void OnUIStackChanged(UIStackChangedEvent evt) { _isAnyUIOpen = evt.AnyUIOpen; _isPaused = evt.AnyUIOpen && _gameService != null && !_gameService.IsMainMenuSceneLoaded; } private void OnHideHUD(InputAction.CallbackContext ctx) { _hudHidden = !_hudHidden; _eventCoordinator?.PublishImmediate(new UIToggleHudEvent(!_hudHidden)); } private void OnPause(InputAction.CallbackContext ctx) { var isMainMenu = _gameService != null && _gameService.IsMainMenuSceneLoaded; if (isMainMenu) { _eventCoordinator?.PublishImmediate(new UIBackRequestedEvent()); return; } _eventCoordinator?.Publish(new PauseButtonClickedEvent()); } private void OnCodex(InputAction.CallbackContext ctx) { if(_gameService.IsMainMenuSceneLoaded) return; _eventCoordinator?.Publish(new ToggleCodexEvent(!_codexShown)); } private void OnClick(InputAction.CallbackContext ctx) { _eventCoordinator?.PublishImmediate(new OnClickEvent(ctx)); } private void OnRightClick(InputAction.CallbackContext ctx) { _eventCoordinator?.PublishImmediate(new OnRightClickEvent(ctx)); } private void OnPoint(InputAction.CallbackContext ctx) { PointerPosition = ctx.ReadValue(); } private void OnVirtualMouse(InputAction.CallbackContext ctx) { _controllerCursorInput = ctx.ReadValue(); } private void OnOpenTools(InputAction.CallbackContext ctx) { if(_gameService.IsMainMenuSceneLoaded) return; _toolScreenShown = !_toolScreenShown; _eventCoordinator?.Publish(new ToggleToolScreenEvent(_toolScreenShown)); } private void OnNextToolClicked(InputAction.CallbackContext ctx) { _eventCoordinator?.Publish(new OnNextToolChangedEvent()); } private void OnPreviousToolClicked(InputAction.CallbackContext ctx) { _eventCoordinator?.Publish(new OnPreviousToolChangedEvent()); } private void OnNextItemClicked(InputAction.CallbackContext ctx) { _eventCoordinator?.Publish(new OnNextItemClickedEvent()); } private void OnPreviousItemClicked(InputAction.CallbackContext ctx) { _eventCoordinator?.Publish(new OnPreviousItemClickedEvent()); } public InputAction GetAction(string actionName) { if (_playerInput == null || _playerInput.actions == null || string.IsNullOrWhiteSpace(actionName)) return null; try { return _playerInput.actions.FindAction(actionName, true); } catch { return null; } } } }