using Jovian.Logger; using System; using System.Collections.Generic; using Jovian.PopupSystem; using Jovian.PopupSystem.UI; using Nox.UI; using UnityEngine; using Object = UnityEngine.Object; namespace Nox.Game.UI { public class PartyGuiView { private readonly Transform portraitsContainer; private readonly PartyMemberSlot slotPrefab; private readonly PortraitsHolder portraitsHolder; private readonly IPopupSystem popupSystem; private readonly List activeSlots = new(); private PartyDefinition trackedParty; private int trackedMemberCount; public PartyGuiView(Transform portraitsContainer, PartyMemberSlot slotPrefab, PortraitsHolder portraitsHolder, IPopupSystem popupSystem = null) { this.portraitsContainer = portraitsContainer; this.slotPrefab = slotPrefab; this.portraitsHolder = portraitsHolder; this.popupSystem = popupSystem; } public void Initialize(PartyDefinition party) { trackedParty = party; trackedMemberCount = 0; RebuildSlots(); } public void Tick() { if(trackedParty == null) { return; } // Rebuild if member count changed if(trackedParty.members.Count != trackedMemberCount) { RebuildSlots(); } // Update dynamic values (health, mana) for(int i = 0; i < activeSlots.Count && i < trackedParty.members.Count; i++) { var member = trackedParty.members[i]; var slot = activeSlots[i]; UpdateSlotStats(slot, member); } } private void RebuildSlots() { // Clear existing foreach(var slot in activeSlots) { Object.Destroy(slot.gameObject); } activeSlots.Clear(); if(trackedParty?.members == null) { return; } trackedMemberCount = trackedParty.members.Count; foreach(var member in trackedParty.members) { var slot = Object.Instantiate(slotPrefab, portraitsContainer); slot.gameObject.SetActive(true); // Portrait if(portraitsHolder != null && portraitsHolder.portraits.Length > 0) { var idx = Mathf.Clamp(member.PortraitIndex, 0, portraitsHolder.portraits.Length - 1); slot.portrait.sprite = portraitsHolder.portraits[idx]; } // Name if(slot.nameText != null) { slot.nameText.text = member.Name; } UpdateSlotStats(slot, member); activeSlots.Add(slot); } // Initialize all popup triggers under the container if(popupSystem != null) { popupSystem.InitializeTriggersInChildren(portraitsContainer, trigger => { var slot = trigger.GetComponentInParent(); if(slot == null) { return; } var slotIndex = activeSlots.IndexOf(slot); if(slotIndex < 0 || slotIndex >= trackedParty.members.Count) { return; } var member = trackedParty.members[slotIndex]; trigger.Initialize(popupSystem, builder => BuildCharacterPopup(builder, member)); }); } } private void BuildCharacterPopup(PopupContentBuilder builder, CharacterDefinition member) { GlobalLogger.LogInfo($"Building character popup for {member.Name}", LogCategory.UI); // Header builder.AddHeader(member.Name); builder.AddText($"{member.Race} {member.Class}", "#CCCCCC"); builder.AddText($"Role: {member.Role}"); builder.AddSeparator(); // Stats if(member.Stats?.stats != null) { foreach(var stat in member.Stats.stats) { if(stat.stat == StatType.None) { continue; } builder.AddStat(stat.stat.ToString(), stat.value); } } builder.AddSeparator(); // Attributes if(member.Attributes?.attributes != null) { foreach(var attr in member.Attributes.attributes) { if(attr.attribute == AttributeType.None) { continue; } builder.AddStat(attr.attribute.ToString(), attr.value); } } builder.AddSeparator(); // Perks if(member.Perks?.perks != null && member.Perks.perks.Count > 0) { builder.AddText("Perks", "#FFD700"); foreach(var perk in member.Perks.perks) { builder.AddText($" {perk.Name}"); } builder.AddSeparator(); } // Modifiers if(member.Modifiers?.modifiers is { Count: > 0 }) { builder.AddText("Modifiers", "#87CEEB"); foreach(var mod in member.Modifiers.modifiers) { var target = mod.Target != null ? mod.Target.ToString() : ""; builder.AddText($" {mod.Name} ({mod.Operation} {mod.Value} {target})"); } } } private static void UpdateSlotStats(PartyMemberSlot slot, CharacterDefinition member) { var health = member.Stats.GetValue(StatType.Health); var mana = member.Stats.GetValue(StatType.Mana); if(slot.healthBar != null) { slot.healthBar.fillAmount = Mathf.Clamp01(health / 100f); } if(slot.healthText != null) { slot.healthText.text = health.ToString(); } if(slot.manaBar != null) { slot.manaBar.fillAmount = Mathf.Clamp01(mana / 100f); } if(slot.manaText != null) { slot.manaText.text = mana.ToString(); } } public void Dispose() { foreach(var slot in activeSlots) { if(slot != null) { Object.Destroy(slot.gameObject); } } activeSlots.Clear(); } } }