using Core; using Input; using Minigames.Airplane.Abilities; using Minigames.Airplane.Core; using TMPro; using UnityEngine; using UnityEngine.UI; namespace Minigames.Airplane.UI { /// /// UI button for activating airplane special abilities. /// Handles input, visual feedback, and cooldown display. /// Implements ITouchInputConsumer to properly handle hold/release for Jet ability. /// public class AirplaneAbilityButton : MonoBehaviour, ITouchInputConsumer { #region Inspector References [Header("UI Components")] [SerializeField] private Button button; [SerializeField] private Image abilityIcon; [SerializeField] private Image cooldownFill; [SerializeField] private TextMeshProUGUI cooldownText; [Header("Debug")] [SerializeField] private bool showDebugLogs; #endregion #region State private BaseAirplaneAbility currentAbility; private AirplaneController currentAirplane; private bool isHoldAbility; // Jet plane needs hold mechanic private bool isHolding; // Track if button is currently being held #endregion #region Lifecycle private void Awake() { if (button != null) { button.onClick.AddListener(OnButtonClick); } // Hide by default gameObject.SetActive(false); } private void Update() { if (currentAbility == null) return; // Update cooldown display if (currentAbility.IsOnCooldown) { // Fill starts at 1 and reduces to 0 over cooldown duration float fillAmount = currentAbility.CooldownRemaining / currentAbility.CooldownDuration; if (cooldownFill != null) { cooldownFill.fillAmount = fillAmount; } // Show timer text if (cooldownText != null) { cooldownText.text = $"{currentAbility.CooldownRemaining:F1}s"; } } else { // Cooldown complete - fill at 0, no text if (cooldownFill != null) cooldownFill.fillAmount = 0f; if (cooldownText != null) cooldownText.text = ""; } } private void OnDestroy() { // Unsubscribe from events if (currentAbility != null) { currentAbility.OnAbilityActivated -= HandleAbilityActivated; currentAbility.OnAbilityDeactivated -= HandleAbilityDeactivated; currentAbility.OnCooldownChanged -= HandleCooldownChanged; } // Unregister from input system if (InputManager.Instance != null) { InputManager.Instance.UnregisterOverrideConsumer(this); } } #endregion #region Public API /// /// Setup button with airplane and ability reference. /// public void Setup(AirplaneController airplane, BaseAirplaneAbility ability) { currentAirplane = airplane; currentAbility = ability; isHolding = false; // Set icon and show immediately if (abilityIcon != null && ability != null) { abilityIcon.sprite = ability.AbilityIcon; abilityIcon.enabled = true; } // Initialize cooldown display if (cooldownFill != null) { cooldownFill.fillAmount = 0f; } if (cooldownText != null) { cooldownText.text = ""; } // Check if this is a hold ability (Jet) isHoldAbility = ability is JetAbility; // Subscribe to ability events if (ability != null) { ability.OnAbilityActivated += HandleAbilityActivated; ability.OnAbilityDeactivated += HandleAbilityDeactivated; ability.OnCooldownChanged += HandleCooldownChanged; if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] Subscribed to ability events for: {ability.AbilityName}"); } } // Show UI gameObject.SetActive(true); if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] Setup complete with ability: {ability?.AbilityName ?? "None"}, Hold: {isHoldAbility}"); } } /// /// Hide and cleanup button. /// public void Hide() { if (currentAbility != null) { currentAbility.OnAbilityActivated -= HandleAbilityActivated; currentAbility.OnAbilityDeactivated -= HandleAbilityDeactivated; currentAbility.OnCooldownChanged -= HandleCooldownChanged; } // Unregister from input system if (InputManager.Instance != null) { InputManager.Instance.UnregisterOverrideConsumer(this); } currentAbility = null; currentAirplane = null; isHolding = false; gameObject.SetActive(false); } #endregion #region Input Handling private void OnButtonClick() { if (currentAirplane == null || currentAbility == null) return; if (!currentAbility.CanActivate) return; // Activate ability currentAirplane.ActivateAbility(); // For hold abilities (Jet), mark as holding and register for input if (isHoldAbility) { isHolding = true; // Register as override consumer to receive hold/release events if (InputManager.Instance != null) { InputManager.Instance.RegisterOverrideConsumer(this); } if (showDebugLogs) { Logging.Debug("[AirplaneAbilityButton] Started holding ability, registered for input"); } } // For non-hold abilities (Bobbing, Drop), this is all we need } #endregion #region Event Handlers private void HandleAbilityActivated(BaseAirplaneAbility ability) { if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] Ability activated: {ability.AbilityName}"); } } private void HandleAbilityDeactivated(BaseAirplaneAbility ability) { if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] Ability deactivated: {ability.AbilityName}"); } } private void HandleCooldownChanged(float remaining, float total) { if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] OnCooldownChanged: remaining={remaining:F2}, total={total:F2}"); } // When cooldown starts (remaining == total), set fill to 1 if (remaining >= total - 0.01f && cooldownFill != null) { cooldownFill.fillAmount = 1f; if (showDebugLogs) { Logging.Debug($"[AirplaneAbilityButton] Cooldown started: {total}s, fill set to 1"); } } } #endregion #region ITouchInputConsumer Implementation public void OnTap(Vector2 position) { // If Jet ability is active (holding), next tap anywhere deactivates it if (isHoldAbility && isHolding) { isHolding = false; currentAirplane?.DeactivateAbility(); // Unregister from input system after tap if (InputManager.Instance != null) { InputManager.Instance.UnregisterOverrideConsumer(this); } if (showDebugLogs) { Logging.Debug("[AirplaneAbilityButton] Tap detected - deactivated Jet ability, unregistered"); } } // Handle as button click for non-hold abilities else if (!isHoldAbility) { OnButtonClick(); } } public void OnHoldStart(Vector2 position) { // Not used - button click handles activation, tap handles deactivation } public void OnHoldMove(Vector2 position) { // Not used } public void OnHoldEnd(Vector2 position) { // Not used - tap handles deactivation for Jet ability } #endregion } }