using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using static OctoCore.OctoSettings; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif namespace OctoCore.Inputs { /// /// Input management module that wraps Unity's Input System. /// Provides enum-based API with automatic enable/disable based on subscriptions. /// [System.Serializable] public class OctoInputs : IOctoModule { [System.Serializable] public class ActionInfo { [HideInInspector] public string Name; [HideInInspector] public string MapName; public bool Enabled; [Tooltip("List of registered callbacks")] public string[] Callbacks; #if ENABLE_INPUT_SYSTEM [System.NonSerialized] public InputActionReference Reference; [System.NonSerialized] public List CallbacksList = new List(); [System.NonSerialized] public List> CallbacksVector2List = new List>(); [System.NonSerialized] public Vector2 LastValue = Vector2.zero; #endif public ActionInfo(string name, string mapName) { Name = name; MapName = mapName; Enabled = false; Callbacks = new string[0]; } } [Header("Actions")] public List Actions = new List(); #if ENABLE_INPUT_SYSTEM private MappingSettings _mappingData; private Dictionary _actionsDict = new Dictionary(); private Dictionary _actionMaps = new Dictionary(); private bool _initialized; #endif public void Init(OctoSettings settings) { #if ENABLE_INPUT_SYSTEM _mappingData = settings.Mapping; if (_mappingData == null) { Debug.LogError("[OctoInputs] No mapping settings found in OctoSettings."); return; } if (_mappingData.InputActionAsset == null) { Debug.LogError("[OctoInputs] No InputActionAsset assigned in OctoSettings."); return; } BuildActions(); _initialized = true; if (OctoCore.Instance.Development.Log) Debug.Log($"[OctoInputs] Initialized with {_actionsDict.Count} actions."); #else Debug.LogWarning("[OctoInputs] Input System Package is not enabled."); #endif // Callback OctoEvents if (OctoEvents.Instance) OctoEvents.Instance.SetInputsInitialized(); } #if ENABLE_INPUT_SYSTEM private void BuildActions() { Actions.Clear(); _actionsDict.Clear(); _actionMaps.Clear(); // Cache all action maps foreach (var map in _mappingData.InputActionAsset.actionMaps) { _actionMaps[map.name] = map; } // Build actions list foreach (var actionData in _mappingData.Actions) { var actionInfo = new ActionInfo(actionData.Name, actionData.MapName); actionInfo.Reference = actionData.Reference; Actions.Add(actionInfo); _actionsDict[actionData.Action] = actionInfo; } } private void EnableAction(OctoActions action) { if (!_actionsDict.TryGetValue(action, out var actionInfo)) return; if (actionInfo.Enabled || actionInfo.Reference == null) return; actionInfo.Reference.action.Enable(); actionInfo.Enabled = true; // Enable parent action map if needed if (_actionMaps.TryGetValue(actionInfo.MapName, out var actionMap)) { if (!actionMap.enabled) { actionMap.Enable(); if (OctoCore.Instance.Development.Log) Debug.Log($"[OctoInputs] Enabled action map: {actionInfo.MapName}"); } } if (OctoCore.Instance.Development.Log) Debug.Log($"[OctoInputs] Enabled action: {action}"); } private void DisableAction(OctoActions action) { if (!_actionsDict.TryGetValue(action, out var actionInfo)) return; if (!actionInfo.Enabled || actionInfo.Reference == null) return; // Check if there are still callbacks int totalCallbacks = actionInfo.CallbacksList.Count + actionInfo.CallbacksVector2List.Count; if (totalCallbacks > 0) return; actionInfo.Reference.action.Disable(); actionInfo.Enabled = false; // Check if we should disable the action map CheckActionMapState(actionInfo.MapName); if (OctoCore.Instance.Development.Log) Debug.Log($"[OctoInputs] Disabled action: {action}"); } private void CheckActionMapState(string mapName) { if (!_actionMaps.TryGetValue(mapName, out var actionMap)) return; // Check if any actions in this map are still enabled bool hasActiveActions = false; foreach (var actionInfo in Actions) { if (actionInfo.MapName == mapName && actionInfo.Enabled) { hasActiveActions = true; break; } } // Disable map if no active actions if (!hasActiveActions && actionMap.enabled) { actionMap.Disable(); if (OctoCore.Instance.Development.Log) Debug.Log($"[OctoInputs] Disabled action map: {mapName}"); } } private void UpdateCallbacksList(OctoActions action) { if (!_actionsDict.TryGetValue(action, out var actionInfo)) return; var callbackNames = new List(); foreach (var callback in actionInfo.CallbacksList) { if (callback != null) callbackNames.Add($"{callback.Method.DeclaringType?.Name}.{callback.Method.Name}"); } foreach (var callback in actionInfo.CallbacksVector2List) { if (callback != null) callbackNames.Add($"{callback.Method.DeclaringType?.Name}.{callback.Method.Name} (Vector2)"); } actionInfo.Callbacks = callbackNames.ToArray(); } #endif public void Tick() { #if ENABLE_INPUT_SYSTEM if (!_initialized) return; // Poll continuous actions (Vector2 callbacks) foreach (var kvp in _actionsDict) { var actionInfo = kvp.Value; // Only poll enabled actions with callbacks if (actionInfo.Enabled && actionInfo.Reference != null && (actionInfo.CallbacksList.Count > 0 || actionInfo.CallbacksVector2List.Count > 0) && actionInfo.Reference.action.IsPressed()) { // Call Action callbacks foreach (var callback in actionInfo.CallbacksList.ToArray()) { callback?.Invoke(); } // Call Vector2 callbacks foreach (var callback in actionInfo.CallbacksVector2List.ToArray()) { Vector2 value = actionInfo.Reference.action.ReadValue(); callback?.Invoke(value); } } } #endif } public void OnSceneChanged(Scene current, Scene next) { } public void OnQuit() { #if ENABLE_INPUT_SYSTEM if (_mappingData != null && _mappingData.InputActionAsset != null) { _mappingData.InputActionAsset.Disable(); } #endif } /// /// Subscribe to an action with no parameters (button press). /// public void SubscribeAction(OctoActions action, Action callback, bool subscribe = true) { #if ENABLE_INPUT_SYSTEM if (!_initialized || callback == null) return; if (!_actionsDict.TryGetValue(action, out var actionInfo)) { Debug.LogWarning($"[OctoInputs] Action {action} not found."); return; } if (actionInfo.Reference == null) { Debug.LogWarning($"[OctoInputs] InputActionReference is null for {action}."); return; } if (subscribe) { if (!actionInfo.CallbacksList.Contains(callback)) { actionInfo.CallbacksList.Add(callback); EnableAction(action); UpdateCallbacksList(action); } } else { actionInfo.CallbacksList.Remove(callback); DisableAction(action); UpdateCallbacksList(action); } #endif } /// /// Subscribe to an action with Vector2 parameter (movement, look). /// Uses continuous polling in Tick() to call the callback while pressed. /// public void SubscribeAction(OctoActions action, Action callback, bool subscribe = true) { #if ENABLE_INPUT_SYSTEM if (!_initialized || callback == null) return; if (!_actionsDict.TryGetValue(action, out var actionInfo)) { Debug.LogWarning($"[OctoInputs] Action {action} not found."); return; } if (actionInfo.Reference == null) { Debug.LogWarning($"[OctoInputs] InputActionReference is null for {action}."); return; } if (subscribe) { if (!actionInfo.CallbacksVector2List.Contains(callback)) { actionInfo.CallbacksVector2List.Add(callback); EnableAction(action); UpdateCallbacksList(action); } } else { actionInfo.CallbacksVector2List.Remove(callback); DisableAction(action); UpdateCallbacksList(action); } #endif } /// /// Unsubscribe from an action. /// public void UnsubscribeAction(OctoActions action, Action callback) { SubscribeAction(action, callback, false); } /// /// Unsubscribe from an action with Vector2. /// public void UnsubscribeAction(OctoActions action, Action callback) { SubscribeAction(action, callback, false); } } }