Files
AppleHillsProduction/Assets/Scripts/Minigames/Airplane/UI/AirplaneAbilityButton.cs
2025-12-18 12:09:58 +01:00

389 lines
13 KiB
C#

using Core;
using Input;
using Minigames.Airplane.Abilities;
using Minigames.Airplane.Core;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Minigames.Airplane.UI
{
/// <summary>
/// UI button for activating airplane special abilities.
/// Handles input, visual feedback, and cooldown display.
/// Implements ITouchInputConsumer to properly handle hold/release for Jet ability.
/// </summary>
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("Active State Visual")]
[Tooltip("Visual element shown when ability is active (e.g., glowing border, pulsing overlay)")]
[SerializeField] private GameObject activeStateVisual;
[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 active state visual by default
if (activeStateVisual != null)
{
activeStateVisual.SetActive(false);
}
// Hide by default
gameObject.SetActive(false);
}
private void Update()
{
if (currentAbility == null) return;
// Priority 1: Show active duration if ability is active
if (currentAbility.IsActive && currentAbility.HasActiveDuration)
{
// NO fill during active duration - only text and active visual
if (cooldownFill != null)
{
cooldownFill.fillAmount = 0f;
}
// Show remaining active time
if (cooldownText != null)
{
cooldownText.text = $"{currentAbility.ActiveDurationRemaining:F1}s";
}
}
// Priority 2: Show cooldown if on cooldown
else if (currentAbility.IsOnCooldown)
{
// Fill always starts at 1 (full) and reduces to 0, regardless of actual cooldown duration
float fillAmount = currentAbility.InitialCooldownDuration > 0f
? currentAbility.CooldownRemaining / currentAbility.InitialCooldownDuration
: 0f;
if (cooldownFill != null)
{
cooldownFill.fillAmount = fillAmount;
}
// Show timer text
if (cooldownText != null)
{
cooldownText.text = $"{currentAbility.CooldownRemaining:F1}s";
}
}
else
{
// Normal state - 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;
currentAbility.OnActiveDurationChanged -= HandleActiveDurationChanged;
}
// Unregister from input system
if (InputManager.Instance != null)
{
InputManager.Instance.UnregisterOverrideConsumer(this);
}
}
#endregion
#region Public API
/// <summary>
/// Setup button with airplane and ability reference.
/// </summary>
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 or Drop - abilities with active duration that can be interrupted)
isHoldAbility = ability is JetAbility || ability is DropAbility;
// Subscribe to ability events
if (ability != null)
{
ability.OnAbilityActivated += HandleAbilityActivated;
ability.OnAbilityDeactivated += HandleAbilityDeactivated;
ability.OnCooldownChanged += HandleCooldownChanged;
ability.OnActiveDurationChanged += HandleActiveDurationChanged;
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}");
}
}
/// <summary>
/// Hide and cleanup button.
/// </summary>
public void Hide()
{
if (currentAbility != null)
{
currentAbility.OnAbilityActivated -= HandleAbilityActivated;
currentAbility.OnAbilityDeactivated -= HandleAbilityDeactivated;
currentAbility.OnCooldownChanged -= HandleCooldownChanged;
currentAbility.OnActiveDurationChanged -= HandleActiveDurationChanged;
}
// Unregister from input system
if (InputManager.Instance != null)
{
InputManager.Instance.UnregisterOverrideConsumer(this);
}
// Hide active state visual
HideActiveState();
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)
{
// Show active state visual if ability has active duration
if (ability.HasActiveDuration)
{
ShowActiveState();
}
if (showDebugLogs)
{
Logging.Debug($"[AirplaneAbilityButton] Ability activated: {ability.AbilityName}");
}
}
private void HandleAbilityDeactivated(BaseAirplaneAbility ability)
{
// Hide active state visual
HideActiveState();
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");
}
}
}
private void HandleActiveDurationChanged(float remaining, float total)
{
if (showDebugLogs)
{
Logging.Debug($"[AirplaneAbilityButton] OnActiveDurationChanged: remaining={remaining:F2}, total={total:F2}");
}
}
#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
#region Active State Visual API
/// <summary>
/// Show active state visual (placeholder API).
/// Override or assign activeStateVisual GameObject in inspector for custom visuals.
/// </summary>
private void ShowActiveState()
{
if (activeStateVisual != null)
{
activeStateVisual.SetActive(true);
if (showDebugLogs)
{
Logging.Debug("[AirplaneAbilityButton] Active state visual shown");
}
}
}
/// <summary>
/// Hide active state visual (placeholder API).
/// </summary>
private void HideActiveState()
{
if (activeStateVisual != null)
{
activeStateVisual.SetActive(false);
if (showDebugLogs)
{
Logging.Debug("[AirplaneAbilityButton] Active state visual hidden");
}
}
}
#endregion
}
}