using System.Collections.Generic; using System.Linq; using Sirenix.Utilities; using UnityEngine; using UnityEngine.UIElements; using VOID.Generic.Objects.Abstract; using VOID.Generic.Objects.Abstract.Events; using VOID.Generic.Objects.Unit; using VOID.Generic.Player; using VOID.Generic.Turn; using VOID.UserInterface.Game.Party; namespace VOID.UserInterface.Game { /// /// Manages TurnQueue elements for current player only:
/// - Displays all participating by their turn order with
/// - Additional info when currently active unit is waiting for its turn
///
public class TurnQueueUI : MonoBehaviour { public static TurnQueueUI current { get; private set; } // Visual Elements private VisualElement _rootElement; private VisualElement _currentQueueElement; private VisualElement _nextQueueElement; private VisualElement _waitingElement; // Portraits for units in current and next queue private readonly Dictionary _currentQueuePortraits = new(); private readonly Dictionary _nextQueuePortraits = new(); // References private UnitObject _activeUnit; private UnitObject _currentUnitTurn; private TurnManager _turnManager; private void Awake() { current = this; var uiDocument = gameObject.GetComponent(); var templateElement = uiDocument.rootVisualElement; _rootElement = templateElement.Q("root-turn-queue"); _currentQueueElement = _rootElement.Q("current-queue"); _nextQueueElement = _rootElement.Q("next-queue"); _waitingElement = _rootElement.Q("waiting"); HideTurnQueue(); _rootElement.dataSource = this; _rootElement.pickingMode = PickingMode.Position; } private void Start() { PlayerController.current.OnActiveUnitChange += OnActiveUnitChange; } private void OnActiveUnitChange(UnitObject nextUnit) { var sameQueue = false; if (_activeUnit) { sameQueue = _activeUnit.basicState.turnManager == nextUnit.basicState.turnManager; _activeUnit.basicEvents.onTurnQueueEnter.RemoveListener(OnActiveUnitTurnQueueEnter); _activeUnit.basicEvents.onTurnQueueLeave.RemoveListener(OnActiveUnitTurnQueueLeave); _activeUnit.basicEvents.onTurnSelfStart.RemoveListener(OnActiveUnitTurnSelfStart); _activeUnit.basicEvents.onTurnSelfEnd.RemoveListener(OnActiveUnitTurnSelfEnd); _activeUnit.basicEvents.onTurnQueueSwap.RemoveListener(OnActiveUnitTurnQueueSwap); } nextUnit.basicEvents.onTurnQueueEnter.AddListener(OnActiveUnitTurnQueueEnter); nextUnit.basicEvents.onTurnQueueLeave.AddListener(OnActiveUnitTurnQueueLeave); nextUnit.basicEvents.onTurnSelfStart.AddListener(OnActiveUnitTurnSelfStart); nextUnit.basicEvents.onTurnSelfEnd.AddListener(OnActiveUnitTurnSelfEnd); nextUnit.basicEvents.onTurnQueueSwap.AddListener(OnActiveUnitTurnQueueSwap); if (!sameQueue) { HideTurnQueue(); if (nextUnit.basicState.turnManager) ShowTurnQueue(nextUnit.basicState.turnManager); } _activeUnit = nextUnit; UpdateWaitingForTurn(); } private void OnActiveUnitTurnQueueSwap(TurnEvent turnEvent) { _turnManager = turnEvent.turnManager; _turnManager.allUnitsInQueue.ForEach((unit, index) => { if (_nextQueuePortraits.Keys.Contains(unit)) return; var nextUnit = _turnManager.allUnitsInQueue.ElementAtOrDefault(index + 1); AddPortrait(unit, false, nextUnit); }); _turnManager.unitsInCurrentQueue.ForEach((unit, index) => { if (_currentQueuePortraits.Keys.Contains(unit)) return; var nextUnit = _turnManager.unitsInCurrentQueue.ElementAtOrDefault(index + 1); AddPortrait(unit, true, nextUnit); }); SetActiveTurn(_turnManager.unitsInCurrentQueue.First()); } private void OnActiveUnitTurnQueueEnter(TurnEvent turnEvent) { ShowTurnQueue(turnEvent.turnManager); UpdateWaitingForTurn(); } private void OnActiveUnitTurnQueueLeave(TurnEvent turnEvent) { HideTurnQueue(); UpdateWaitingForTurn(); } private void OnActiveUnitTurnSelfStart(TurnEvent turnEvent) => UpdateWaitingForTurn(); private void OnActiveUnitTurnSelfEnd(TurnEvent turnEvent) => UpdateWaitingForTurn(); private void OnUnitEnter(AbstractObject obj) { if (obj is not UnitObject unit) return; var beforeUnitInCurrent = _turnManager.unitsInCurrentQueue.ElementAtOrDefault( _turnManager.unitsInCurrentQueue.IndexOf(unit) + 1); var beforeUnitInNext = _turnManager.allUnitsInQueue.ElementAtOrDefault( _turnManager.allUnitsInQueue.IndexOf(unit) + 1); AddPortrait(unit, true, beforeUnitInCurrent); AddPortrait(unit, false, beforeUnitInNext); } private void OnUnitLeave(AbstractObject obj) { if (obj is not UnitObject unit) return; RemovePortrait(unit); } private void OnNextTurn() { if (_currentUnitTurn) { _currentQueuePortraits.GetValueOrDefault(_currentUnitTurn)?.Destroy(); _currentQueuePortraits.Remove(_currentUnitTurn); } _currentUnitTurn = _turnManager.currentUnitTurn; SetActiveTurn(_currentUnitTurn); } private void OnNextQueue() { _currentUnitTurn = null; _currentQueuePortraits.Values.ForEach(portrait => portrait.Destroy()); _currentQueuePortraits.Clear(); _turnManager.unitsInCurrentQueue.ForEach(unit => AddPortrait(unit)); } private void ShowTurnQueue(TurnManager turnManager) { HideTurnQueue(); _turnManager = turnManager; _rootElement.style.display = DisplayStyle.Flex; _turnManager.unitsInCurrentQueue.ForEach(unit => AddPortrait(unit)); _turnManager.allUnitsInQueue.ForEach(unit => AddPortrait(unit, false)); if (_turnManager.currentUnitTurn) SetActiveTurn(_turnManager.currentUnitTurn); _turnManager.OnNextTurn += OnNextTurn; _turnManager.OnNextQueue += OnNextQueue; _turnManager.OnObjectEnter += OnUnitEnter; _turnManager.OnObjectLeave += OnUnitLeave; _turnManager.OnNextQueueOrderChange += OnNextQueueOrderChange; _turnManager.OnCurrentQueueOrderChange += OnCurrentQueueOrderChange; } private void HideTurnQueue() { _rootElement.style.display = DisplayStyle.None; _currentQueuePortraits.Values.ForEach(unitPortrait => unitPortrait.Destroy()); _currentQueuePortraits.Clear(); _currentQueueElement.Clear(); _nextQueuePortraits.Values.ForEach(unitPortrait => unitPortrait.Destroy()); _nextQueuePortraits.Clear(); _nextQueueElement.Clear(); if (!_turnManager) return; _turnManager.OnNextTurn -= OnNextTurn; _turnManager.OnNextQueue -= OnNextQueue; _turnManager.OnObjectEnter -= OnUnitEnter; _turnManager.OnObjectLeave -= OnUnitLeave; _turnManager.OnNextQueueOrderChange -= OnNextQueueOrderChange; _turnManager.OnCurrentQueueOrderChange -= OnCurrentQueueOrderChange; _currentUnitTurn = null; _turnManager = null; } private void AddPortrait(UnitObject unit, bool putIntoCurrent = true, UnitObject putBefore = null) { var element = putIntoCurrent ? _currentQueueElement : _nextQueueElement; var dict = putIntoCurrent ? _currentQueuePortraits : _nextQueuePortraits; var unitPortrait = new UnitPortraitUI(); unitPortrait.InitNew(element); unitPortrait.SetUnit(unit); if (putBefore) { var behindElement = dict.GetValueOrDefault(putBefore).templateElement; unitPortrait.templateElement.PlaceBehind(behindElement); } dict.Add(unit, unitPortrait); } private void RemovePortrait(UnitObject unit) { _currentQueuePortraits.GetValueOrDefault(unit)?.Destroy(); _nextQueuePortraits.GetValueOrDefault(unit)?.Destroy(); _currentQueuePortraits.Remove(unit); _nextQueuePortraits.Remove(unit); } private void MovePortrait(UnitObject unit, bool putIntoCurrent = true, UnitObject putBefore = null) { var dict = putIntoCurrent ? _currentQueuePortraits : _nextQueuePortraits; if (putBefore) { var behindElement = dict.GetValueOrDefault(putBefore).templateElement; dict.GetValueOrDefault(unit).templateElement.PlaceBehind(behindElement); } else { dict.GetValueOrDefault(unit).templateElement.BringToFront(); } } private void OnNextQueueOrderChange(UnitObject unit, int newIndex) { MovePortrait(unit, false, _turnManager.allUnitsInQueue.ElementAtOrDefault(newIndex + 1)); } private void OnCurrentQueueOrderChange(UnitObject unit, int newIndex) { MovePortrait(unit, true, _turnManager.unitsInCurrentQueue.ElementAtOrDefault(newIndex + 1)); } private void SetActiveTurn(UnitObject unit) { _currentQueueElement.Q(className: "active-turn")?.RemoveFromClassList("active-turn"); _currentQueuePortraits.GetValueOrDefault(unit)?.templateElement.AddToClassList("active-turn"); } /// /// Makes TurnQueue's waiting element visible if it is not active unit's turn. /// private void UpdateWaitingForTurn() { var isWaiting = _activeUnit.basicState.turnManager && _activeUnit.basicState.turnManager.currentUnitTurn != _activeUnit; _waitingElement.style.display = isWaiting ? DisplayStyle.Flex : DisplayStyle.None; } } }