using UnityEngine; using UnityEngine.UI; using TMPro; using System.Collections.Generic; /// /// TeamHUDPanel — FRAG-style circular portrait HUD showing team health and character switching. /// /// SETUP (Inspector): /// 1. Create your HUD layout manually — place circular Image slots wherever you want. /// 2. Assign them into the lists below: /// - characterPortraits[] — Image where the character icon/portrait sprite will go /// - healthDialImages[] — Image used as a radial-fill health ring around each portrait /// - dialBackgrounds[] — (optional) background ring images behind the health dials /// - nameLabels[] — (optional) TMP text for character names /// 3. The script fills sprites and updates health dials every frame. /// 4. Click/tap a portrait to switch control to that character. /// /// Uses CanvasGroup to hide initially — stays alive for Initialize() call. /// public class TeamHUDPanel : MonoBehaviour { [Header("Character Portraits (assign in order: index 0 = team slot 0, etc.)")] [Tooltip("Image components where character icon sprites will be placed")] [SerializeField] private List characterPortraits = new List(); [Header("Health Dial Rings (same index order as portraits)")] [Tooltip("Image components used as radial-fill health rings — set to Filled/Radial360 at runtime")] [SerializeField] private List healthDialImages = new List(); [Header("Dial Backgrounds (optional, same index order)")] [Tooltip("Background ring images behind each health dial")] [SerializeField] private List dialBackgrounds = new List(); [Header("Name Labels (optional, same index order)")] [SerializeField] private List nameLabels = new List(); [Header("Dead Overlay (optional, same index order)")] [Tooltip("GameObjects to show when a character is dead (e.g. a dark overlay with X)")] [SerializeField] private List deadOverlays = new List(); [Header("Highlight Border (optional, same index order)")] [Tooltip("Image used as a highlight border — only visible on the active character")] [SerializeField] private List highlightBorders = new List(); [Header("Dial Sprite")] [Tooltip("Sprite for the health dial ring. If empty, tries to copy from ImportUIPanel's ProgressDial.")] [SerializeField] private Sprite dialSprite; [Header("Audio")] [SerializeField] private AudioClip switchSFX; [SerializeField] private AudioClip buttonHoverSFX; [SerializeField] private float sfxVolume = 1f; [Header("Colors")] [SerializeField] private Color healthFullColor = new Color(0.30f, 0.87f, 0.30f, 1f); [SerializeField] private Color healthLowColor = new Color(0.96f, 0.26f, 0.21f, 1f); [SerializeField] private Color highlightColor = new Color(1f, 0.84f, 0f, 1f); [SerializeField] private Color defaultBorderColor = new Color(0.3f, 0.3f, 0.3f, 0.6f); // runtime private List _characters = new List(); private List _healthComps = new List(); private int _currentHighlight = -1; private CanvasGroup _canvasGroup; private AudioSource _audio; private static TeamHUDPanel _instance; public static TeamHUDPanel Instance => _instance; private void Awake() { _instance = this; _canvasGroup = GetComponent(); if (_canvasGroup == null) _canvasGroup = gameObject.AddComponent(); _audio = GetComponent(); if (_audio == null) _audio = gameObject.AddComponent(); _audio.playOnAwake = false; _audio.spatialBlend = 0f; // Hide via CanvasGroup until Initialize is called HideImmediate(); // Try to find dial sprite from ImportUIPanel if not assigned if (dialSprite == null) { var importPanel = GameObject.Find("ImportUIPanel"); if (importPanel != null) { var dial = importPanel.transform.Find("ProgressDial"); if (dial != null) { var img = dial.GetComponent(); if (img != null) dialSprite = img.sprite; } } } } private void OnEnable() { GameEvents.OnCharacterSwitched += OnCharacterSwitched; } private void OnDisable() { GameEvents.OnCharacterSwitched -= OnCharacterSwitched; } // ─── Public API ────────────────────────────────────────── /// /// Initialize the HUD with the player team characters. /// Call after characters are spawned (from CashSystemManager.SpawnTeams). /// public void Initialize(List playerTeamCharacters, int currentControlledIndex) { DevLog.Log($"[TeamHUDPanel] Initialize with {playerTeamCharacters?.Count ?? 0} characters, active={currentControlledIndex}"); _characters.Clear(); _healthComps.Clear(); if (playerTeamCharacters == null || playerTeamCharacters.Count == 0) return; int count = Mathf.Min(playerTeamCharacters.Count, characterPortraits.Count); for (int i = 0; i < count; i++) { GameObject character = playerTeamCharacters[i]; _characters.Add(character); _healthComps.Add(character != null ? character.GetComponent() : null); // Set portrait sprite if (i < characterPortraits.Count && characterPortraits[i] != null && character != null) { Sprite icon = GetCharacterIcon(character); if (icon != null) { characterPortraits[i].sprite = icon; characterPortraits[i].color = Color.white; characterPortraits[i].preserveAspect = true; } // Add click handler int capturedIndex = i; Button btn = characterPortraits[i].GetComponent