Files
2026-07-09 21:33:33 +02:00

160 lines
8.0 KiB
C#

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 = "<b>{0}:<b> ";
[SerializeField] private string STATUS_START_MESSAGE = "Got status <u>{0}</u> for <u>{1} turns</u>.";
[SerializeField] private string STATUS_END_MESSAGE = "Status <u>{0}</u> ended.";
[SerializeField] private string DEATH_MESSAGE = "Died!";
[SerializeField] private string DAMAGED_MESSAGE = "Received <u>{0} {1}</u> damage from <u>{2}</u>.";
[SerializeField] private string DAMAGE_MESSAGE = "Dealt <u>{0} {1}</u> damage to <u>{2}</u>.";
[SerializeField] private string ABILITY_CAST_MESSAGE = "Casted <u>{0}</u>.";
[SerializeField] private string TAKE_MESSAGE = "Took <u>{0}</u>.";
[SerializeField] private string DROP_MESSAGE = "Dropped <u>{0}</u>.";
[SerializeField] private string EQUIP_MESSAGE = "Equipped <u>{0}</u>.";
[SerializeField] private string UNEQUIP_MESSAGE = "Unequipped <u>{0}</u>.";
[SerializeField] private string USE_MESSAGE = "Used <u>{0}</u>.";
[SerializeField] private string ACTION_ERROR_MESSAGE = "Action prevented: <u>{0}</u>.";
// Message template
public VisualTreeAsset messageTemplate => _messageTemplate;
[SerializeField, Required] private VisualTreeAsset _messageTemplate;
// Visual Elements
private VisualElement _messageWrapper;
private Scroller _scroller;
private readonly List<GameLogMessageUI> _messages = new();
// Units which we listen to
private UnitObject _mainUnit;
private UnitObject _subUnit;
private void Awake()
{
current = this;
var uiDocument = gameObject.GetComponent<UIDocument>();
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($"<color=#D33>{richText}</color>");
}
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)));
}
}