253 lines
8.6 KiB
C#
253 lines
8.6 KiB
C#
using Core;
|
|
using Core.Lifecycle;
|
|
using Minigames.FortFight.Core;
|
|
using Minigames.FortFight.Data;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Minigames.FortFight.UI
|
|
{
|
|
/// <summary>
|
|
/// Generic reusable ammunition button that displays projectile data.
|
|
/// Shows icon, availability state, cooldown progress, and turns remaining.
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Initialize the button with projectile config and system references.
|
|
/// Call this after instantiation to configure the button.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update all visual elements based on current state
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Called when button is clicked - selects this ammo type
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|
|
|