using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.UIElements; using VOID.Generic.Objects.Abstract.Events; using VOID.Generic.Objects.Item.Events; using VOID.Generic.Objects.Unit; using VOID.Generic.Objects.Unit.Actions.Exceptions; using VOID.Generic.Objects.Unit.Events; using VOID.Generic.Objects.Usable.Events; using VOID.Generic.Objects.Wearable.Events; using VOID.Generic.Player; namespace VOID.UserInterface.Game.GameLog { public class GameLogUI : MonoBehaviour { public static GameLogUI current { get; private set; } // Configuration [SerializeField, MinValue(10)] private int _messageLimit = 100; [SerializeField] private string SENDER_PREFIX = "{0}: "; [SerializeField] private string STATUS_START_MESSAGE = "Got status {0} for {1} turns."; [SerializeField] private string STATUS_END_MESSAGE = "Status {0} ended."; [SerializeField] private string DEATH_MESSAGE = "Died!"; [SerializeField] private string DAMAGED_MESSAGE = "Received {0} {1} damage from {2}."; [SerializeField] private string DAMAGE_MESSAGE = "Dealt {0} {1} damage to {2}."; [SerializeField] private string ABILITY_CAST_MESSAGE = "Casted {0}."; [SerializeField] private string TAKE_MESSAGE = "Took {0}."; [SerializeField] private string DROP_MESSAGE = "Dropped {0}."; [SerializeField] private string EQUIP_MESSAGE = "Equipped {0}."; [SerializeField] private string UNEQUIP_MESSAGE = "Unequipped {0}."; [SerializeField] private string USE_MESSAGE = "Used {0}."; [SerializeField] private string ACTION_ERROR_MESSAGE = "Action prevented: {0}."; // Message template public VisualTreeAsset messageTemplate => _messageTemplate; [SerializeField, Required] private VisualTreeAsset _messageTemplate; // Visual Elements private VisualElement _messageWrapper; private Scroller _scroller; private readonly List _messages = new(); // Units which we listen to private UnitObject _mainUnit; private UnitObject _subUnit; private void Awake() { current = this; var uiDocument = gameObject.GetComponent(); var templateElement = uiDocument.rootVisualElement; _messageWrapper = templateElement.Q("message-wrapper"); _messageWrapper.Clear(); } private void Start() { PlayerController.current.OnMainUnitChange += OnMainUnitChange; PlayerController.current.OnSubUnitChange += OnSubUnitChange; } private void OnMainUnitChange(UnitObject unit) { if (_mainUnit) StopListenFor(_mainUnit); _mainUnit = unit; if (unit) ListenFor(unit); } private void OnSubUnitChange(UnitObject unit) { if (_subUnit) StopListenFor(_subUnit); _subUnit = unit; if (unit) ListenFor(unit); } private void ListenFor(UnitObject unit) { unit.basicEvents.onStatusStart.AddListener(OnStatusStart); unit.basicEvents.onStatusEnd.AddListener(OnStatusEnd); unit.basicEvents.onDeathAfter.AddListener(OnDeathAfter); unit.basicEvents.onDamagedAfter.AddListener(OnDamagedAfter); unit.unitEvents.onDamageAfter.AddListener(OnDamageAfter); unit.unitEvents.onStartAbilityAfter.AddListener(OnAbilityCastAfter); unit.unitEvents.onTakeAfter.AddListener(OnTakeAfter); unit.unitEvents.onDropAfter.AddListener(OnDropAfter); unit.unitEvents.onEquipAfter.AddListener(OnEquipAfter); unit.unitEvents.onUnEquipAfter.AddListener(OnUnEquipAfter); unit.unitEvents.onUseAfter.AddListener(OnUseAfter); unit.unitEvents.onActionError.AddListener(OnActionError); } private void StopListenFor(UnitObject unit) { unit.basicEvents.onStatusStart.RemoveListener(OnStatusStart); unit.basicEvents.onStatusEnd.RemoveListener(OnStatusEnd); unit.basicEvents.onDeathAfter.RemoveListener(OnDeathAfter); unit.basicEvents.onDamagedAfter.RemoveListener(OnDamagedAfter); unit.unitEvents.onDamageAfter.RemoveListener(OnDamageAfter); unit.unitEvents.onStartAbilityAfter.RemoveListener(OnAbilityCastAfter); unit.unitEvents.onTakeAfter.RemoveListener(OnTakeAfter); unit.unitEvents.onDropAfter.RemoveListener(OnDropAfter); unit.unitEvents.onEquipAfter.RemoveListener(OnEquipAfter); unit.unitEvents.onUnEquipAfter.RemoveListener(OnUnEquipAfter); unit.unitEvents.onUseAfter.RemoveListener(OnUseAfter); unit.unitEvents.onActionError.RemoveListener(OnActionError); } private void AddError(string richText) { AddMessage($"{richText}"); } private void AddMessage(string richText) { var message = new GameLogMessageUI(); message.InitNew(_messageWrapper, richText); _messages.Add(message); if (_messages.Count > _messageLimit) RemoveLastMessage(); } private void RemoveLastMessage() { var lastMessage = _messages.Last(); lastMessage.Destroy(); _messages.RemoveAt(_messages.Count-1); } /* EVENTS BELOW */ /* for register and unregister */ private void OnStatusStart(StatusEvent e) { if (e.status.hidden) return; AddMessage(string.Format(SENDER_PREFIX, e.obj.basicData.finalName) + string.Format(STATUS_START_MESSAGE, e.status.displayName, e.status.durationInTurns)); } private void OnStatusEnd(StatusEvent e) { if (e.status.hidden) return; AddMessage(string.Format(SENDER_PREFIX, e.obj.basicData.finalName) + string.Format(STATUS_END_MESSAGE, e.status.displayName)); } private void OnDeathAfter(DeathEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.obj.basicData.finalName) + string.Format(DEATH_MESSAGE)); private void OnDamagedAfter(DamageEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.target.basicData.finalName) + string.Format(DAMAGED_MESSAGE, e.amount, e.damageType, e.attacker?.basicData.finalName ?? "unknown")); private void OnDamageAfter(DamageEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.attacker.basicData.finalName) + string.Format(DAMAGE_MESSAGE, e.amount, e.damageType, e.target.basicData.finalName)); private void OnAbilityCastAfter(AbilityCastEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.caster.basicData.finalName) + string.Format(ABILITY_CAST_MESSAGE, e.ability.displayName)); private void OnTakeAfter(PickEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.unit.basicData.finalName) + string.Format(TAKE_MESSAGE, e.item.basicData.finalName)); private void OnDropAfter(DropEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.unit.basicData.finalName) + string.Format(DROP_MESSAGE, e.item.basicData.finalName)); private void OnEquipAfter(EquipEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.unit.basicData.finalName) + string.Format(EQUIP_MESSAGE, e.wearable.basicData.finalName)); private void OnUnEquipAfter(UnEquipEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.unit.basicData.finalName) + string.Format(UNEQUIP_MESSAGE, e.wearable.basicData.finalName)); private void OnUseAfter(UseEvent e) => AddMessage(string.Format(SENDER_PREFIX, e.usedBy.basicData.finalName) + string.Format(USE_MESSAGE, e.usedObject.basicData.finalName)); private void OnActionError(ActionErrorEvent e) => AddError(string.Format(SENDER_PREFIX, e.unit.basicData.finalName) + string.Format(ACTION_ERROR_MESSAGE, UnitActionErrorMessage.Get(e.cause))); } }