using Core; using Core.Lifecycle; using Minigames.FortFight.Core; using Minigames.FortFight.Data; using TMPro; using UnityEngine; using UnityEngine.UI; namespace Minigames.FortFight.UI { /// /// Generic reusable ammunition button that displays projectile data. /// Shows icon, availability state, cooldown progress, and turns remaining. /// public class AmmoButton : ManagedBehaviour { #region Inspector References [Header("UI Components")] [Tooltip("Icon image for the projectile")] [SerializeField] private Image iconImage; [Tooltip("Background overlay that greys out the entire button when on cooldown")] [SerializeField] private Image cooldownBackgroundImage; [Tooltip("Radial fill overlay for cooldown visualization")] [SerializeField] private Image cooldownFillImage; [Tooltip("Text displaying turns remaining")] [SerializeField] private TextMeshProUGUI turnsRemainingText; [Tooltip("Button component")] [SerializeField] private Button button; [Tooltip("Visual indicator for selected state (optional border/glow)")] [SerializeField] private GameObject selectedIndicator; #endregion #region State private ProjectileConfig projectileConfig; private AmmunitionManager ammunitionManager; private SlingshotController slingshotController; private int playerIndex; private bool isSelected; #endregion #region Initialization /// /// Initialize the button with projectile config and system references. /// Call this after instantiation to configure the button. /// public void Initialize(ProjectileConfig config, AmmunitionManager ammoManager, SlingshotController slingshot, int playerIdx) { projectileConfig = config; ammunitionManager = ammoManager; slingshotController = slingshot; playerIndex = playerIdx; // Setup UI from projectile config if (iconImage != null && config.icon != null) { iconImage.sprite = config.icon; } // Setup cooldown background (hidden by default) if (cooldownBackgroundImage != null) { cooldownBackgroundImage.gameObject.SetActive(false); } // Setup cooldown fill (hidden by default) if (cooldownFillImage != null) { cooldownFillImage.fillAmount = 0f; cooldownFillImage.type = Image.Type.Filled; cooldownFillImage.fillMethod = Image.FillMethod.Radial360; cooldownFillImage.fillOrigin = (int)Image.Origin360.Top; cooldownFillImage.gameObject.SetActive(false); } // Setup turns text (hidden by default) if (turnsRemainingText != null) { turnsRemainingText.gameObject.SetActive(false); } // Setup button if (button != null) { button.onClick.AddListener(OnButtonClicked); } // Subscribe to ammunition events if (ammunitionManager != null) { ammunitionManager.OnAmmoSelected += HandleAmmoSelected; ammunitionManager.OnAmmoCooldownStarted += HandleCooldownStarted; ammunitionManager.OnAmmoCooldownCompleted += HandleCooldownCompleted; } // Initial update UpdateVisuals(); } internal override void OnManagedDestroy() { base.OnManagedDestroy(); // Unsubscribe from events if (ammunitionManager != null) { ammunitionManager.OnAmmoSelected -= HandleAmmoSelected; ammunitionManager.OnAmmoCooldownStarted -= HandleCooldownStarted; ammunitionManager.OnAmmoCooldownCompleted -= HandleCooldownCompleted; } // Remove button listener if (button != null) { button.onClick.RemoveListener(OnButtonClicked); } } #endregion #region Update private void Update() { // Update visuals every frame UpdateVisuals(); } /// /// Update all visual elements based on current state /// private void UpdateVisuals() { if (projectileConfig == null || ammunitionManager == null) return; // Get current cooldown state for this player int turnsRemaining = ammunitionManager.GetCooldownRemaining(projectileConfig.projectileType, playerIndex); bool isAvailable = ammunitionManager.IsAmmoAvailable(projectileConfig.projectileType, playerIndex); bool onCooldown = turnsRemaining > 0; // Show/hide cooldown background overlay if (cooldownBackgroundImage != null) { cooldownBackgroundImage.gameObject.SetActive(onCooldown); } // Update cooldown fill (0 = no fill, 1 = full fill) if (cooldownFillImage != null) { if (onCooldown && projectileConfig.cooldownTurns > 0) { float fillAmount = (float)turnsRemaining / projectileConfig.cooldownTurns; cooldownFillImage.fillAmount = fillAmount; cooldownFillImage.gameObject.SetActive(true); } else { cooldownFillImage.gameObject.SetActive(false); } } // Update turns remaining text if (turnsRemainingText != null) { if (onCooldown) { turnsRemainingText.text = turnsRemaining.ToString(); turnsRemainingText.gameObject.SetActive(true); } else { turnsRemainingText.gameObject.SetActive(false); } } // Update button interactability if (button != null) { button.interactable = isAvailable; } // Update selected indicator if (selectedIndicator != null) { selectedIndicator.SetActive(isSelected); } } #endregion #region Button Click /// /// Called when button is clicked - selects this ammo type /// private void OnButtonClicked() { if (projectileConfig == null || ammunitionManager == null) return; // Try to select this ammo type for this player bool selected = ammunitionManager.SelectAmmo(projectileConfig.projectileType, playerIndex); if (selected && slingshotController != null) { // Update slingshot with new ammo config slingshotController.SetAmmo(projectileConfig); Logging.Debug($"[AmmoButton] Player {playerIndex} selected {projectileConfig.displayName}"); } } #endregion #region Event Handlers private void HandleAmmoSelected(ProjectileType selectedType, int selectedPlayerIndex) { // Only update if this event is for our player if (selectedPlayerIndex != playerIndex) return; // Update selected state - check if this is our player's current selection isSelected = (selectedType == projectileConfig.projectileType); } private void HandleCooldownStarted(ProjectileType type, int cooldownTurns) { // Visual update handled in UpdateVisuals() } private void HandleCooldownCompleted(ProjectileType type) { // Visual update handled in UpdateVisuals() if (type == projectileConfig.projectileType) { Logging.Debug($"[AmmoButton] {projectileConfig.displayName} ready!"); } } #endregion } }