using System; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using Sirenix.Utilities; using UnityEngine; using UnityEngine.UIElements; using VOID.Generic.Objects.Unit; using VOID.Generic.Objects.Unit.Actions; using VOID.Generic.Objects.Usable; using VOID.Generic.Player.CursorOperation; using VOID.Generic.Player.Input; using VOID.Generic.Player.Util; using VOID.UserInterface.Game; using VOID.UserInterface.Game.AlertUI; using VOID.UserInterface.Game.Util; using VOID.ScriptableObjects.Abilities; using PlayerInputManager = VOID.Generic.Player.Input.PlayerInputManager; namespace VOID.Generic.Player { /// /// Manages ALL inputs from player and which player can control. /// [DefaultExecutionOrder(-1)] public class PlayerController : MonoBehaviour { public static PlayerController current { get; private set; } [Title("Unit currently controlled by player")] public UnitObject activeUnit { get; private set; } [Title("Units which player can control")] [field: SerializeField] public UnitObject mainUnit { get; private set; } [field: SerializeField] public UnitObject subUnit { get; private set; } // Event public event Action OnActiveUnitChange; public event Action OnMainUnitChange; public event Action OnSubUnitChange; // References private AlertUI _alertUI; private ActionBarSlotsUI _actionBarSlotsUI; private PlayerInputManager _playerInputManager; private ShortcutsInputManager _shortcutsInputManager; private readonly List _playerOperations = new(); private void Awake() { current = this; // Get all classes that inherits from "AbstractCursorOperation" // BUT! In that order where children will be before parents // So when we can choose first matching OrdersList for that specific type AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => type.IsSubclassOf(typeof(AbstractCursorOperation)) && type.IsClass) .Select(Activator.CreateInstance) .Cast() .ForEach(ordersList => { var insertIndex = _playerOperations.FindIndex(otherOrderList => ordersList.ForType() != null && otherOrderList.ForType() != null && ordersList.ForType().IsSubclassOf(otherOrderList.ForType())); if (insertIndex == -1) insertIndex = _playerOperations.Count; _playerOperations.Insert(insertIndex, ordersList); }); } private void Start() { // references _playerInputManager = PlayerInputManager.current; _shortcutsInputManager = ShortcutsInputManager.current; _alertUI = AlertUI.current; _actionBarSlotsUI = ActionBarSlotsUI.current; // left and right mouse buttons events _playerInputManager.OnDefaultUseUI += OnPlayerDefaultUseOnUI; _playerInputManager.OnDefaultAlternateUseUI += OnPlayerDefaultAlternateUseOnUI; _playerInputManager.OnDefaultUse += OnPlayerDefaultUseOnScene; _playerInputManager.OnContextUse += OnPlayerContextUse; // Bind ESC _shortcutsInputManager.menu.performed += _ => OnESC(); // Bind events for action bar slot shortcuts _shortcutsInputManager.actionBarSlot1.performed += _ => OnActionBarUseShortcut(0); _shortcutsInputManager.actionBarSlot2.performed += _ => OnActionBarUseShortcut(1); _shortcutsInputManager.actionBarSlot3.performed += _ => OnActionBarUseShortcut(2); _shortcutsInputManager.actionBarSlot4.performed += _ => OnActionBarUseShortcut(3); _shortcutsInputManager.actionBarSlot5.performed += _ => OnActionBarUseShortcut(4); _shortcutsInputManager.actionBarSlot6.performed += _ => OnActionBarUseShortcut(5); _shortcutsInputManager.actionBarSlot7.performed += _ => OnActionBarUseShortcut(6); _shortcutsInputManager.actionBarSlot8.performed += _ => OnActionBarUseShortcut(7); _shortcutsInputManager.actionBarSlot9.performed += _ => OnActionBarUseShortcut(8); _shortcutsInputManager.actionBarSlot10.performed += _ => OnActionBarUseShortcut(9); _shortcutsInputManager.actionBarSlot11.performed += _ => OnActionBarUseShortcut(10); _shortcutsInputManager.actionBarSlot12.performed += _ => OnActionBarUseShortcut(11); _shortcutsInputManager.actionBarSlot13.performed += _ => OnActionBarUseShortcut(12); _shortcutsInputManager.actionBarSlot14.performed += _ => OnActionBarUseShortcut(13); _shortcutsInputManager.actionBarSlot15.performed += _ => OnActionBarUseShortcut(14); _shortcutsInputManager.actionBarSlot16.performed += _ => OnActionBarUseShortcut(15); _shortcutsInputManager.actionBarSlot17.performed += _ => OnActionBarUseShortcut(16); _shortcutsInputManager.actionBarSlot18.performed += _ => OnActionBarUseShortcut(17); _shortcutsInputManager.actionBarSlot19.performed += _ => OnActionBarUseShortcut(18); _shortcutsInputManager.actionBarSlot20.performed += _ => OnActionBarUseShortcut(19); _shortcutsInputManager.actionBarSlot21.performed += _ => OnActionBarUseShortcut(20); _shortcutsInputManager.actionBarSlot22.performed += _ => OnActionBarUseShortcut(21); _shortcutsInputManager.actionBarSlot23.performed += _ => OnActionBarUseShortcut(22); _shortcutsInputManager.actionBarSlot24.performed += _ => OnActionBarUseShortcut(23); _shortcutsInputManager.actionBarSlot25.performed += _ => OnActionBarUseShortcut(24); } public void SetMainUnit(UnitObject mainUnit) { this.mainUnit = mainUnit; OnMainUnitChange?.Invoke(mainUnit); } public void SetSubUnit(UnitObject subUnit) { this.subUnit = subUnit; OnSubUnitChange?.Invoke(subUnit); } public void SetMainUnitActive() => SetUnitActive(mainUnit); public void SetSubUnitActive() => SetUnitActive(subUnit); private void SetUnitActive(UnitObject unit) { if (activeUnit) _alertUI.StopListenFor(activeUnit); activeUnit = unit; _alertUI.ListenFor(activeUnit); OnActiveUnitChange?.Invoke(activeUnit); } #region CURSOR - DEFAULT & CONTEXT /// /// Shortcuts from player to use or /// from in currently . /// /// Index of slot to use private void OnActionBarUseShortcut(int index) { var slotUI = _actionBarSlotsUI.slotsUI[index]; if (slotUI == null) return; if (!slotUI.item && !slotUI.ability) return; activeUnit.OrderStop(); if (slotUI.item) activeUnit.Order(new UseAction(activeUnit, slotUI.item)); if (slotUI.ability) activeUnit.Order(new CastingAction(activeUnit, slotUI.ability)); } private void OnPlayerDefaultAlternateUseOnUI(Vector2 cachedPosition, List cachedElements) { HandlePlayerDefaultUseOnUI(cachedElements, true); } private void OnPlayerDefaultUseOnUI(Vector2 cachedPosition, List cachedElements) { HandlePlayerDefaultUseOnUI(cachedElements); } private void HandlePlayerDefaultUseOnUI(List cachedElements, bool isAlternate = false) { var select = SelectUtil.SelectFromUI(cachedElements); // For QoL ActionBar slots can be used with single click if (select.actionBarSlotsUI) isAlternate = false; HandlePlayerDefaultUse(select, isAlternate); } private void OnPlayerDefaultUseOnScene() { HandlePlayerDefaultUse(SelectUtil.SelectFromScene()); } /// /// Handle default use from both SCENE and UI /// private void HandlePlayerDefaultUse(SelectUtilResult select, bool isAlternate = false) { if (!activeUnit || !activeUnit.unitState.isControllable) return; if (!select.hit) return; var operation = _playerOperations.FirstOrDefault(operation => operation.Check(select)); operation?.DoDefault(select, isAlternate); if (operation == null) Debug.LogError($"No Cursor Operation defined for: {select.selectObject.GetType().GetNiceName()}"); } private void OnPlayerContextUse() { if (!activeUnit || !activeUnit.unitState.isControllable) return; var select = SelectUtil.Select(); if (!select.hit) return; var operation = _playerOperations.FirstOrDefault(operation => operation.Check(select)); operation?.DoContext(select); if (operation == null) Debug.LogError($"No Cursor Operation defined for: {select.selectObject.GetType().GetNiceName()}"); } #endregion #region ESC private void OnESC() { if (TryStopCasting()) return; if (TryCloseWindowUI()) return; if (TryTogglePauseMenu()) return; } private bool TryStopCasting() { if (!activeUnit) return false; if (activeUnit.unitActionQueue.actionCurrent is not CastingAction) return false; activeUnit.OrderStop(); return true; } private bool TryCloseWindowUI() { if (WindowUI.openWindows.IsNullOrEmpty()) return false; WindowUI.openWindows.First().Close(); return true; } private bool TryTogglePauseMenu() { PauseMenuUI.current.Toggle(); return true; } #endregion } }