Working MVP
This commit is contained in:
252
Assets/Scripts/Minigames/FortFight/UI/AmmoButton.cs
Normal file
252
Assets/Scripts/Minigames/FortFight/UI/AmmoButton.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/Minigames/FortFight/UI/AmmoButton.cs.meta
Normal file
3
Assets/Scripts/Minigames/FortFight/UI/AmmoButton.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d18726bad651464fbc4e49f8c95c0c37
|
||||
timeCreated: 1764770308
|
||||
171
Assets/Scripts/Minigames/FortFight/UI/AmmunitionPanel.cs
Normal file
171
Assets/Scripts/Minigames/FortFight/UI/AmmunitionPanel.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Minigames.FortFight.Core;
|
||||
using Minigames.FortFight.Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.FortFight.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages ammunition UI panel for a specific player.
|
||||
/// Shows/hides based on turn state and initializes buttons with player context.
|
||||
/// </summary>
|
||||
public class AmmunitionPanel : ManagedBehaviour
|
||||
{
|
||||
#region Inspector References
|
||||
|
||||
[Header("Player Configuration")]
|
||||
[Tooltip("Which player this panel belongs to (0 = Player 1, 1 = Player 2)")]
|
||||
[SerializeField] private int playerIndex = 0;
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("Ammunition manager (shared between both players)")]
|
||||
[SerializeField] private AmmunitionManager ammunitionManager;
|
||||
|
||||
[Tooltip("This player's slingshot controller")]
|
||||
[SerializeField] private SlingshotController slingshotController;
|
||||
|
||||
[Tooltip("Turn manager to subscribe to turn events")]
|
||||
[SerializeField] private TurnManager turnManager;
|
||||
|
||||
[Header("UI")]
|
||||
[Tooltip("Array of ammo buttons in this panel")]
|
||||
[SerializeField] private AmmoButton[] ammoButtons;
|
||||
|
||||
[Tooltip("Root GameObject to show/hide entire panel")]
|
||||
[SerializeField] private GameObject panelRoot;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Validate references
|
||||
if (ammunitionManager == null)
|
||||
{
|
||||
Logging.Error($"[AmmunitionPanel] Player {playerIndex}: Ammunition manager not assigned!");
|
||||
}
|
||||
|
||||
if (slingshotController == null)
|
||||
{
|
||||
Logging.Error($"[AmmunitionPanel] Player {playerIndex}: Slingshot controller not assigned!");
|
||||
}
|
||||
|
||||
if (turnManager == null)
|
||||
{
|
||||
Logging.Error($"[AmmunitionPanel] Player {playerIndex}: Turn manager not assigned!");
|
||||
}
|
||||
|
||||
if (ammoButtons == null || ammoButtons.Length == 0)
|
||||
{
|
||||
Logging.Warning($"[AmmunitionPanel] Player {playerIndex}: No ammo buttons assigned!");
|
||||
}
|
||||
|
||||
// Use assigned panelRoot or fall back to this GameObject
|
||||
if (panelRoot == null)
|
||||
{
|
||||
panelRoot = gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
// Initialize ammo buttons with player context
|
||||
InitializeButtons();
|
||||
|
||||
// Subscribe to turn events
|
||||
if (turnManager != null)
|
||||
{
|
||||
turnManager.OnTurnStarted += HandleTurnStarted;
|
||||
}
|
||||
|
||||
// Start hidden
|
||||
SetPanelVisibility(false);
|
||||
}
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
{
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Unsubscribe from events
|
||||
if (turnManager != null)
|
||||
{
|
||||
turnManager.OnTurnStarted -= HandleTurnStarted;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all ammo buttons with player-specific configuration
|
||||
/// </summary>
|
||||
private void InitializeButtons()
|
||||
{
|
||||
if (ammunitionManager == null || slingshotController == null || ammoButtons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get available projectile types from settings
|
||||
var availableTypes = ammunitionManager.GetAvailableProjectileTypes();
|
||||
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
Logging.Error($"[AmmunitionPanel] Player {playerIndex}: Could not get FortFightSettings!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ammoButtons.Length && i < availableTypes.Count; i++)
|
||||
{
|
||||
if (ammoButtons[i] != null)
|
||||
{
|
||||
var config = settings.GetProjectileConfig(availableTypes[i]);
|
||||
if (config != null)
|
||||
{
|
||||
ammoButtons[i].Initialize(config, ammunitionManager, slingshotController, playerIndex);
|
||||
Logging.Debug($"[AmmunitionPanel] Player {playerIndex}: Initialized button for {config.displayName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Turn Events
|
||||
|
||||
/// <summary>
|
||||
/// Called when any player's turn starts - show/hide panel accordingly
|
||||
/// </summary>
|
||||
private void HandleTurnStarted(PlayerData player, TurnState turnState)
|
||||
{
|
||||
// Only show panel when it's this player's turn (not during transitions)
|
||||
bool shouldShow = player.PlayerIndex == playerIndex &&
|
||||
(turnState == TurnState.PlayerOneTurn || turnState == TurnState.PlayerTwoTurn);
|
||||
|
||||
SetPanelVisibility(shouldShow);
|
||||
|
||||
if (shouldShow)
|
||||
{
|
||||
Logging.Debug($"[AmmunitionPanel] Showing panel for Player {playerIndex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show or hide the entire panel
|
||||
/// </summary>
|
||||
private void SetPanelVisibility(bool visible)
|
||||
{
|
||||
if (panelRoot != null)
|
||||
{
|
||||
panelRoot.SetActive(visible);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1963617e4d104c199c3a66d671b8d8a2
|
||||
timeCreated: 1764771029
|
||||
@@ -11,15 +11,13 @@ namespace Minigames.FortFight.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Main gameplay UI page for Fort Fight minigame.
|
||||
/// Displays turn info and allows player to take actions (stubbed for Phase 1).
|
||||
/// Displays turn info. Turn actions now handled via slingshot input system.
|
||||
/// </summary>
|
||||
public class GameplayPage : UIPage
|
||||
{
|
||||
[Header("UI Elements")]
|
||||
[SerializeField] private TextMeshProUGUI turnIndicatorText;
|
||||
[SerializeField] private TextMeshProUGUI currentPlayerText;
|
||||
[SerializeField] private Button takeActionButton;
|
||||
[SerializeField] private TextMeshProUGUI actionButtonText;
|
||||
|
||||
[Header("Optional Visual Elements")]
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
@@ -37,11 +35,7 @@ namespace Minigames.FortFight.UI
|
||||
// Validate references
|
||||
ValidateReferences();
|
||||
|
||||
// Set up button
|
||||
if (takeActionButton != null)
|
||||
{
|
||||
takeActionButton.onClick.AddListener(OnTakeActionClicked);
|
||||
}
|
||||
// Note: takeActionButton is no longer used - turns handled via slingshot input
|
||||
|
||||
// Set up canvas group
|
||||
if (canvasGroup == null)
|
||||
@@ -55,7 +49,7 @@ namespace Minigames.FortFight.UI
|
||||
base.OnManagedStart();
|
||||
|
||||
// Get turn manager reference
|
||||
turnManager = FindObjectOfType<TurnManager>();
|
||||
turnManager = FindFirstObjectByType<TurnManager>();
|
||||
|
||||
if (turnManager != null)
|
||||
{
|
||||
@@ -80,15 +74,8 @@ namespace Minigames.FortFight.UI
|
||||
Logging.Warning("[GameplayPage] Current player text not assigned!");
|
||||
}
|
||||
|
||||
if (takeActionButton == null)
|
||||
{
|
||||
Logging.Error("[GameplayPage] Take action button not assigned!");
|
||||
}
|
||||
|
||||
if (actionButtonText == null)
|
||||
{
|
||||
Logging.Warning("[GameplayPage] Action button text not assigned!");
|
||||
}
|
||||
// Note: takeActionButton and actionButtonText are no longer used
|
||||
// Turns are now handled automatically via slingshot input system
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -147,11 +134,6 @@ namespace Minigames.FortFight.UI
|
||||
{
|
||||
aiTurnPanel.SetActive(true);
|
||||
}
|
||||
|
||||
if (takeActionButton != null)
|
||||
{
|
||||
takeActionButton.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
else if (turnState == TurnState.PlayerOneTurn || turnState == TurnState.PlayerTwoTurn)
|
||||
{
|
||||
@@ -165,48 +147,11 @@ namespace Minigames.FortFight.UI
|
||||
{
|
||||
aiTurnPanel.SetActive(false);
|
||||
}
|
||||
|
||||
if (takeActionButton != null)
|
||||
{
|
||||
takeActionButton.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
if (actionButtonText != null)
|
||||
{
|
||||
actionButtonText.text = "Take Action (Stubbed)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Button Callbacks
|
||||
|
||||
/// <summary>
|
||||
/// Called when player clicks the "Take Action" button
|
||||
/// STUBBED for Phase 1 - just logs and ends turn
|
||||
/// </summary>
|
||||
private void OnTakeActionClicked()
|
||||
{
|
||||
if (turnManager == null)
|
||||
{
|
||||
Logging.Error("[GameplayPage] Cannot take action - TurnManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerData currentPlayer = turnManager.CurrentPlayer;
|
||||
|
||||
// STUBBED: Log the action
|
||||
Logging.Debug($"[GameplayPage] {currentPlayer.PlayerName} takes action! (STUBBED - no actual combat yet)");
|
||||
|
||||
// TODO Phase 3: Replace with actual slingshot/shooting interface
|
||||
|
||||
// End the turn
|
||||
turnManager.EndTurn();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Transitions
|
||||
|
||||
protected override void DoTransitionIn(System.Action onComplete)
|
||||
@@ -252,12 +197,6 @@ namespace Minigames.FortFight.UI
|
||||
turnManager.OnTurnStarted -= OnTurnStarted;
|
||||
turnManager.OnTurnEnded -= OnTurnEnded;
|
||||
}
|
||||
|
||||
// Unsubscribe from button
|
||||
if (takeActionButton != null)
|
||||
{
|
||||
takeActionButton.onClick.RemoveListener(OnTakeActionClicked);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user