Files
VOIDRPG/Assets/90 - Scripts/Generic/Player/PlayerController.cs
T
2026-07-09 21:33:33 +02:00

244 lines
10 KiB
C#

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
{
/// <summary>
/// Manages ALL inputs from player and <see cref="UnitObject"/> which player can control.
/// </summary>
[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<UnitObject> OnActiveUnitChange;
public event Action<UnitObject> OnMainUnitChange;
public event Action<UnitObject> OnSubUnitChange;
// References
private AlertUI _alertUI;
private ActionBarSlotsUI _actionBarSlotsUI;
private PlayerInputManager _playerInputManager;
private ShortcutsInputManager _shortcutsInputManager;
private readonly List<AbstractCursorOperation> _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<AbstractCursorOperation>()
.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
/// <summary>
/// Shortcuts from player to use <see cref="UsableObject"/> or <see cref="BasicAbility"/>
/// from <see cref="UnitObjectActionBar"/> in currently <see cref="activeUnit"/>.
/// </summary>
/// <param name="index">Index of slot to use</param>
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<VisualElement> cachedElements)
{
HandlePlayerDefaultUseOnUI(cachedElements, true);
}
private void OnPlayerDefaultUseOnUI(Vector2 cachedPosition, List<VisualElement> cachedElements)
{
HandlePlayerDefaultUseOnUI(cachedElements);
}
private void HandlePlayerDefaultUseOnUI(List<VisualElement> 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());
}
/// <summary>
/// Handle default use from both SCENE and UI
/// </summary>
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
}
}