Implement Fort Fight minigame #75
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,8 @@ namespace Minigames.FortFight.AI
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
// Get reference to turn manager
|
||||
turnManager = FindFirstObjectByType<TurnManager>();
|
||||
// Get reference to turn manager via singleton
|
||||
turnManager = TurnManager.Instance;
|
||||
|
||||
if (turnManager == null)
|
||||
{
|
||||
|
||||
@@ -11,9 +11,27 @@ namespace Minigames.FortFight.Core
|
||||
/// <summary>
|
||||
/// Manages ammunition selection and cooldowns for Fort Fight.
|
||||
/// Tracks which ammo types are available and handles cooldown timers.
|
||||
/// Singleton pattern for easy access throughout the game.
|
||||
/// </summary>
|
||||
public class AmmunitionManager : ManagedBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
private static AmmunitionManager _instance;
|
||||
public static AmmunitionManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = FindFirstObjectByType<AmmunitionManager>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constants
|
||||
|
||||
private const int MaxPlayers = 2; // Support 2 players (indices 0 and 1)
|
||||
@@ -85,6 +103,20 @@ namespace Minigames.FortFight.Core
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Register singleton
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Logging.Warning("[AmmunitionManager] Multiple instances detected! Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
@@ -10,9 +10,27 @@ namespace Minigames.FortFight.Core
|
||||
/// Manages camera states and transitions for the Fort Fight minigame.
|
||||
/// Subscribes to turn events and switches camera views accordingly.
|
||||
/// Uses Cinemachine for smooth camera blending.
|
||||
/// Singleton pattern for easy access.
|
||||
/// </summary>
|
||||
public class CameraController : ManagedBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
private static CameraController _instance;
|
||||
public static CameraController Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = FindFirstObjectByType<CameraController>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inspector References
|
||||
|
||||
[Header("Cinemachine Cameras")]
|
||||
@@ -28,9 +46,7 @@ namespace Minigames.FortFight.Core
|
||||
[Tooltip("Camera that follows projectiles in flight (should have CinemachineFollow component)")]
|
||||
[SerializeField] private CinemachineCamera projectileCamera;
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("Turn manager to subscribe to turn events")]
|
||||
[SerializeField] private TurnManager turnManager;
|
||||
// Note: TurnManager accessed via singleton
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -49,6 +65,15 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Register singleton
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Logging.Warning("[CameraController] Multiple instances detected! Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
|
||||
// Validate references
|
||||
if (wideViewCamera == null)
|
||||
{
|
||||
@@ -70,21 +95,17 @@ namespace Minigames.FortFight.Core
|
||||
Logging.Warning("[CameraController] Projectile camera not assigned - projectiles won't be followed!");
|
||||
}
|
||||
|
||||
if (turnManager == null)
|
||||
{
|
||||
Logging.Error("[CameraController] Turn manager not assigned!");
|
||||
}
|
||||
}
|
||||
|
||||
internal override void OnManagedStart()
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
// Subscribe to turn events
|
||||
if (turnManager != null)
|
||||
// Subscribe to turn events via singleton
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.OnTurnStarted += HandleTurnStarted;
|
||||
turnManager.OnTurnEnded += HandleTurnEnded;
|
||||
TurnManager.Instance.OnTurnStarted += HandleTurnStarted;
|
||||
TurnManager.Instance.OnTurnEnded += HandleTurnEnded;
|
||||
|
||||
Logging.Debug("[CameraController] Subscribed to turn events");
|
||||
}
|
||||
@@ -95,13 +116,18 @@ namespace Minigames.FortFight.Core
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
{
|
||||
if (_instance == this)
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Unsubscribe from events
|
||||
if (turnManager != null)
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.OnTurnStarted -= HandleTurnStarted;
|
||||
turnManager.OnTurnEnded -= HandleTurnEnded;
|
||||
TurnManager.Instance.OnTurnStarted -= HandleTurnStarted;
|
||||
TurnManager.Instance.OnTurnEnded -= HandleTurnEnded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,11 +285,6 @@ namespace Minigames.FortFight.Core
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
// Auto-find TurnManager if not assigned
|
||||
if (turnManager == null)
|
||||
{
|
||||
turnManager = FindFirstObjectByType<TurnManager>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -25,13 +25,14 @@ namespace Minigames.FortFight.Core
|
||||
#region Inspector References
|
||||
|
||||
[Header("Core Systems")]
|
||||
[SerializeField] private TurnManager turnManager;
|
||||
[SerializeField] private FortFightAIController aiController;
|
||||
[SerializeField] private FortManager fortManager;
|
||||
|
||||
[Header("UI References")]
|
||||
[SerializeField] private ModeSelectionPage modeSelectionPage;
|
||||
[SerializeField] private GameplayPage gameplayPage;
|
||||
[SerializeField] private UI.GameOverUI gameOverUI;
|
||||
|
||||
// Note: TurnManager and FortManager accessed via singletons
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -60,10 +61,23 @@ namespace Minigames.FortFight.Core
|
||||
private PlayerData playerOne;
|
||||
private PlayerData playerTwo;
|
||||
private bool isGameActive = false;
|
||||
private float gameStartTime = 0f;
|
||||
|
||||
public FortFightGameMode CurrentGameMode => currentGameMode;
|
||||
public bool IsGameActive => isGameActive;
|
||||
|
||||
/// <summary>
|
||||
/// Get elapsed game time in seconds since game started
|
||||
/// </summary>
|
||||
public float ElapsedGameTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isGameActive) return 0f;
|
||||
return Time.time - gameStartTime;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
@@ -97,9 +111,19 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
base.OnManagedDestroy();
|
||||
|
||||
if (_instance == this)
|
||||
// Unsubscribe from fort defeated events
|
||||
var fortManager = FortManager.Instance;
|
||||
if (fortManager != null)
|
||||
{
|
||||
_instance = null;
|
||||
if (fortManager.PlayerFort != null)
|
||||
{
|
||||
fortManager.PlayerFort.OnFortDefeated -= OnFortDefeated;
|
||||
}
|
||||
|
||||
if (fortManager.EnemyFort != null)
|
||||
{
|
||||
fortManager.EnemyFort.OnFortDefeated -= OnFortDefeated;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear events
|
||||
@@ -114,11 +138,6 @@ namespace Minigames.FortFight.Core
|
||||
|
||||
private void ValidateReferences()
|
||||
{
|
||||
if (turnManager == null)
|
||||
{
|
||||
Logging.Error("[FortFightGameManager] TurnManager reference not assigned!");
|
||||
}
|
||||
|
||||
if (aiController == null)
|
||||
{
|
||||
Logging.Warning("[FortFightGameManager] AIController reference not assigned! AI mode will not work.");
|
||||
@@ -133,6 +152,11 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
Logging.Error("[FortFightGameManager] GameplayPage reference not assigned!");
|
||||
}
|
||||
|
||||
if (gameOverUI == null)
|
||||
{
|
||||
Logging.Error("[FortFightGameManager] GameOverUI reference not assigned!");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -151,10 +175,16 @@ namespace Minigames.FortFight.Core
|
||||
Logging.Debug("[FortFightGameManager] Showing mode selection page");
|
||||
}
|
||||
|
||||
// Hide other UI pages
|
||||
if (gameplayPage != null)
|
||||
{
|
||||
gameplayPage.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (gameOverUI != null)
|
||||
{
|
||||
gameOverUI.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,15 +222,15 @@ namespace Minigames.FortFight.Core
|
||||
|
||||
Logging.Debug($"[FortFightGameManager] Players initialized - P1: {playerOne.PlayerName}, P2: {playerTwo.PlayerName}");
|
||||
|
||||
// Spawn forts for both players
|
||||
if (fortManager != null)
|
||||
// Spawn forts for both players via singleton
|
||||
if (FortManager.Instance != null)
|
||||
{
|
||||
fortManager.SpawnForts();
|
||||
FortManager.Instance.SpawnForts();
|
||||
Logging.Debug("[FortFightGameManager] Forts spawned for both players");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[FortFightGameManager] FortManager not assigned! Forts will not spawn.");
|
||||
Logging.Warning("[FortFightGameManager] FortManager not found! Forts will not spawn.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +251,11 @@ namespace Minigames.FortFight.Core
|
||||
gameplayPage.TransitionIn();
|
||||
}
|
||||
|
||||
// Initialize turn manager
|
||||
if (turnManager != null)
|
||||
// Initialize turn manager via singleton
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.Initialize(playerOne, playerTwo);
|
||||
turnManager.StartGame();
|
||||
TurnManager.Instance.Initialize(playerOne, playerTwo);
|
||||
TurnManager.Instance.StartGame();
|
||||
}
|
||||
|
||||
// Initialize AI if in single player mode
|
||||
@@ -234,29 +264,122 @@ namespace Minigames.FortFight.Core
|
||||
aiController.Initialize();
|
||||
}
|
||||
|
||||
// Subscribe to fort defeated events (may need to wait for forts to spawn)
|
||||
StartCoroutine(SubscribeToFortEventsWhenReady());
|
||||
|
||||
isGameActive = true;
|
||||
gameStartTime = Time.time; // Start tracking elapsed time
|
||||
OnGameStarted?.Invoke();
|
||||
|
||||
Logging.Debug("[FortFightGameManager] Game started!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End the game
|
||||
/// Wait for forts to be spawned and ready, then subscribe to their defeat events
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator SubscribeToFortEventsWhenReady()
|
||||
{
|
||||
Logging.Debug("[FortFightGameManager] Waiting for forts to be ready...");
|
||||
|
||||
var fortManager = FortManager.Instance;
|
||||
if (fortManager == null)
|
||||
{
|
||||
Logging.Error("[FortFightGameManager] FortManager not found! Cannot subscribe to fort events.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Wait up to 5 seconds for forts to spawn
|
||||
float timeout = 5f;
|
||||
float elapsed = 0f;
|
||||
|
||||
while ((fortManager.PlayerFort == null || fortManager.EnemyFort == null) && elapsed < timeout)
|
||||
{
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
elapsed += 0.1f;
|
||||
}
|
||||
|
||||
if (fortManager.PlayerFort == null || fortManager.EnemyFort == null)
|
||||
{
|
||||
Logging.Error($"[FortFightGameManager] Forts not ready after {timeout}s! PlayerFort: {fortManager.PlayerFort != null}, EnemyFort: {fortManager.EnemyFort != null}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Subscribe to both forts
|
||||
Logging.Debug($"[FortFightGameManager] Forts ready! Subscribing to defeat events...");
|
||||
|
||||
fortManager.PlayerFort.OnFortDefeated += OnFortDefeated;
|
||||
fortManager.EnemyFort.OnFortDefeated += OnFortDefeated;
|
||||
|
||||
Logging.Debug($"[FortFightGameManager] Successfully subscribed to fort defeat events: PlayerFort={fortManager.PlayerFort.FortName}, EnemyFort={fortManager.EnemyFort.FortName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when any fort is defeated
|
||||
/// </summary>
|
||||
private void OnFortDefeated()
|
||||
{
|
||||
Logging.Debug("[FortFightGameManager] Fort defeated, ending game...");
|
||||
EndGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End the game and show game over UI
|
||||
/// </summary>
|
||||
public void EndGame()
|
||||
{
|
||||
if (!isGameActive)
|
||||
{
|
||||
Logging.Warning("[FortFightGameManager] EndGame called but game is not active");
|
||||
return;
|
||||
}
|
||||
|
||||
isGameActive = false;
|
||||
|
||||
if (turnManager != null)
|
||||
// Stop turn manager
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.SetGameOver();
|
||||
TurnManager.Instance.SetGameOver();
|
||||
}
|
||||
|
||||
// Manage UI transitions
|
||||
ShowGameOver();
|
||||
|
||||
OnGameEnded?.Invoke();
|
||||
|
||||
Logging.Debug("[FortFightGameManager] Game ended");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show game over UI and hide gameplay UI
|
||||
/// </summary>
|
||||
private void ShowGameOver()
|
||||
{
|
||||
// Hide gameplay page
|
||||
if (gameplayPage != null)
|
||||
{
|
||||
gameplayPage.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Show game over UI
|
||||
if (gameOverUI != null)
|
||||
{
|
||||
gameOverUI.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Error("[FortFightGameManager] Cannot show game over UI - reference not assigned!");
|
||||
}
|
||||
|
||||
// Switch camera to wide view
|
||||
var cameraController = CameraController.Instance;
|
||||
if (cameraController != null)
|
||||
{
|
||||
cameraController.ShowWideView();
|
||||
}
|
||||
|
||||
Logging.Debug("[FortFightGameManager] Game over UI shown");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,27 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages fort prefab spawning and references during gameplay.
|
||||
/// Singleton pattern for easy access to fort references.
|
||||
/// </summary>
|
||||
public class FortManager : ManagedBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
private static FortManager _instance;
|
||||
public static FortManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = FindFirstObjectByType<FortManager>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inspector Properties
|
||||
|
||||
[Header("Fort Prefabs")]
|
||||
@@ -56,6 +74,15 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Register singleton
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Logging.Warning("[FortManager] Multiple instances detected! Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
|
||||
// Validate spawn points
|
||||
if (playerSpawnPoint == null)
|
||||
{
|
||||
|
||||
@@ -10,9 +10,27 @@ namespace Minigames.FortFight.Core
|
||||
/// Manages turn order and turn state for Fort Fight minigame.
|
||||
/// Handles transitions between Player One, Player Two/AI turns.
|
||||
/// Manages turn actions and input delegation.
|
||||
/// Singleton pattern for easy access to turn state.
|
||||
/// </summary>
|
||||
public class TurnManager : ManagedBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
private static TurnManager _instance;
|
||||
public static TurnManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = FindFirstObjectByType<TurnManager>();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inspector References
|
||||
|
||||
[Header("Slingshot Controllers")]
|
||||
@@ -23,12 +41,11 @@ namespace Minigames.FortFight.Core
|
||||
[SerializeField] private SlingshotController playerTwoSlingshotController;
|
||||
|
||||
[Header("Systems")]
|
||||
[Tooltip("Ammunition manager")]
|
||||
[SerializeField] private AmmunitionManager ammunitionManager;
|
||||
|
||||
[Tooltip("Camera controller for projectile tracking")]
|
||||
[SerializeField] private CameraController cameraController;
|
||||
|
||||
// Note: AmmunitionManager accessed via singleton (AmmunitionManager.Instance)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -75,6 +92,15 @@ namespace Minigames.FortFight.Core
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Register singleton
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Logging.Warning("[TurnManager] Multiple instances detected! Destroying duplicate.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
|
||||
// Validate references
|
||||
if (playerOneSlingshotController == null)
|
||||
{
|
||||
@@ -86,11 +112,6 @@ namespace Minigames.FortFight.Core
|
||||
Logging.Error("[TurnManager] Player Two slingshot controller not assigned!");
|
||||
}
|
||||
|
||||
if (ammunitionManager == null)
|
||||
{
|
||||
Logging.Error("[TurnManager] Ammunition manager not assigned!");
|
||||
}
|
||||
|
||||
if (cameraController == null)
|
||||
{
|
||||
Logging.Warning("[TurnManager] Camera controller not assigned - projectiles won't be followed by camera!");
|
||||
@@ -186,12 +207,12 @@ namespace Minigames.FortFight.Core
|
||||
}
|
||||
|
||||
// Create and execute turn action with player index
|
||||
currentTurnAction = new ProjectileTurnAction(activeSlingshot, ammunitionManager, cameraController, currentPlayer.PlayerIndex);
|
||||
currentTurnAction = new ProjectileTurnAction(activeSlingshot, AmmunitionManager.Instance, cameraController, currentPlayer.PlayerIndex);
|
||||
|
||||
// Set current ammo on slingshot for this player
|
||||
if (ammunitionManager != null)
|
||||
if (AmmunitionManager.Instance != null)
|
||||
{
|
||||
ProjectileConfig currentAmmo = ammunitionManager.GetSelectedAmmoConfig(currentPlayer.PlayerIndex);
|
||||
ProjectileConfig currentAmmo = AmmunitionManager.Instance.GetSelectedAmmoConfig(currentPlayer.PlayerIndex);
|
||||
if (currentAmmo != null)
|
||||
{
|
||||
activeSlingshot.SetAmmo(currentAmmo);
|
||||
@@ -253,9 +274,9 @@ namespace Minigames.FortFight.Core
|
||||
OnTurnEnded?.Invoke(currentPlayer);
|
||||
|
||||
// Decrement ammunition cooldowns for this player
|
||||
if (ammunitionManager != null)
|
||||
if (AmmunitionManager.Instance != null)
|
||||
{
|
||||
ammunitionManager.DecrementCooldowns(currentPlayer.PlayerIndex);
|
||||
AmmunitionManager.Instance.DecrementCooldowns(currentPlayer.PlayerIndex);
|
||||
}
|
||||
|
||||
// Enter transition state (triggers wide view camera via OnTurnStarted)
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace Minigames.FortFight.Fort
|
||||
[SerializeField] private BlockSize size = BlockSize.Medium;
|
||||
[SerializeField] private bool isWeakPoint = false;
|
||||
|
||||
[Tooltip("Fixed HP value for this block (default: 10)")]
|
||||
[SerializeField] private float blockHp = 10f;
|
||||
|
||||
[Header("Weak Point Settings (if applicable)")]
|
||||
[Tooltip("Visual indicator shown in editor/game for weak points")]
|
||||
[SerializeField] private GameObject weakPointVisualIndicator;
|
||||
@@ -146,15 +149,8 @@ namespace Minigames.FortFight.Fort
|
||||
|
||||
private void CalculateHp()
|
||||
{
|
||||
// Get material config
|
||||
var materialConfig = CachedSettings.GetMaterialConfig(material);
|
||||
float baseMaterialHp = materialConfig?.baseHp ?? 20f;
|
||||
|
||||
// Get size config
|
||||
var sizeConfig = CachedSettings.GetSizeConfig(size);
|
||||
float sizeMultiplier = sizeConfig?.hpMultiplier ?? 1f;
|
||||
|
||||
maxHp = baseMaterialHp * sizeMultiplier;
|
||||
// Use fixed block HP value (default 10)
|
||||
maxHp = blockHp;
|
||||
currentHp = maxHp;
|
||||
|
||||
Logging.Debug($"[FortBlock] {gameObject.name} initialized: {material} {size}, HP: {maxHp}");
|
||||
|
||||
@@ -52,6 +52,10 @@ namespace Minigames.FortFight.Fort
|
||||
public int InitialBlockCount => initialBlockCount;
|
||||
public bool IsDefeated { get; private set; }
|
||||
|
||||
// Aliases for consistency
|
||||
public float MaxHp => maxFortHp;
|
||||
public float CurrentHp => currentFortHp;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private State
|
||||
@@ -171,6 +175,9 @@ namespace Minigames.FortFight.Fort
|
||||
RegisterBlock(block);
|
||||
}
|
||||
|
||||
// Step 3: Initialize current HP to match max HP (sum of all blocks)
|
||||
currentFortHp = maxFortHp;
|
||||
|
||||
initialBlockCount = blocks.Count;
|
||||
Logging.Debug($"[FortController] {fortName} - Initialized and registered {blocks.Count} blocks, Total HP: {maxFortHp:F0}");
|
||||
}
|
||||
@@ -181,7 +188,7 @@ namespace Minigames.FortFight.Fort
|
||||
/// </summary>
|
||||
private void RegisterWithManager()
|
||||
{
|
||||
Core.FortManager manager = FindFirstObjectByType<Core.FortManager>();
|
||||
Core.FortManager manager = Core.FortManager.Instance;
|
||||
|
||||
if (manager == null)
|
||||
{
|
||||
@@ -207,8 +214,9 @@ namespace Minigames.FortFight.Fort
|
||||
if (!blocks.Contains(block))
|
||||
{
|
||||
blocks.Add(block);
|
||||
|
||||
// Only add to max HP, current HP will be calculated once at end of initialization
|
||||
maxFortHp += block.MaxHp;
|
||||
currentFortHp += block.MaxHp;
|
||||
|
||||
// Subscribe to block events
|
||||
block.OnBlockDestroyed += HandleBlockDestroyed;
|
||||
@@ -259,9 +267,8 @@ namespace Minigames.FortFight.Fort
|
||||
// Remove from list
|
||||
blocks.Remove(block);
|
||||
|
||||
// Update current HP
|
||||
currentFortHp -= blockMaxHp;
|
||||
currentFortHp = Mathf.Max(0f, currentFortHp);
|
||||
// Recalculate HP by summing all remaining blocks (consistent calculation method)
|
||||
RecalculateFortHp();
|
||||
|
||||
// Notify listeners
|
||||
OnBlockDestroyed?.Invoke(block);
|
||||
@@ -279,7 +286,38 @@ namespace Minigames.FortFight.Fort
|
||||
private void HandleBlockDamaged(float currentBlockHp, float maxBlockHp)
|
||||
{
|
||||
// Block damaged but not destroyed
|
||||
// Could add visual feedback here if needed
|
||||
Logging.Debug($"[FortController] {fortName} - Block damaged! CurrentBlockHP: {currentBlockHp}/{maxBlockHp}");
|
||||
|
||||
// Recalculate current fort HP based on all block HP
|
||||
RecalculateFortHp();
|
||||
|
||||
// Notify UI to update
|
||||
int listenerCount = OnFortDamaged?.GetInvocationList()?.Length ?? 0;
|
||||
Logging.Debug($"[FortController] {fortName} - Firing OnFortDamaged event. HP: {HpPercentage:F1}%. Listeners: {listenerCount}");
|
||||
OnFortDamaged?.Invoke(0f, HpPercentage);
|
||||
|
||||
// Check defeat condition after damage
|
||||
CheckDefeatCondition();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculate total fort HP by summing all block HP
|
||||
/// </summary>
|
||||
private void RecalculateFortHp()
|
||||
{
|
||||
currentFortHp = 0f;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block != null)
|
||||
{
|
||||
currentFortHp += block.CurrentHp;
|
||||
}
|
||||
}
|
||||
|
||||
if (showDebugInfo)
|
||||
{
|
||||
Logging.Debug($"[FortController] {fortName} - HP recalculated: {currentFortHp:F0}/{maxFortHp:F0} ({HpPercentage:F1}%)");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -288,17 +326,32 @@ namespace Minigames.FortFight.Fort
|
||||
|
||||
private void CheckDefeatCondition()
|
||||
{
|
||||
if (IsDefeated) return;
|
||||
if (IsDefeated)
|
||||
{
|
||||
Logging.Debug($"[FortController] {fortName} - Already defeated, skipping check");
|
||||
return;
|
||||
}
|
||||
|
||||
float defeatThreshold = CachedSettings?.FortDefeatThreshold ?? 0.3f;
|
||||
float defeatThresholdPercent = defeatThreshold * 100f;
|
||||
|
||||
// Defeat if HP below threshold
|
||||
if (HpPercentage < defeatThresholdPercent)
|
||||
Logging.Debug($"[FortController] {fortName} - Checking defeat: HP={currentFortHp:F1}/{maxFortHp:F1} ({HpPercentage:F1}%) vs threshold={defeatThresholdPercent:F1}%");
|
||||
|
||||
// Defeat if HP at or below threshold
|
||||
if (HpPercentage <= defeatThresholdPercent)
|
||||
{
|
||||
IsDefeated = true;
|
||||
Logging.Debug($"[FortController] {fortName} DEFEATED! Final HP: {HpPercentage:F1}% (threshold: {defeatThresholdPercent:F1}%)");
|
||||
|
||||
int listeners = OnFortDefeated?.GetInvocationList()?.Length ?? 0;
|
||||
Logging.Debug($"[FortController] {fortName} DEFEATED! Final HP: {HpPercentage:F1}% (threshold: {defeatThresholdPercent:F1}%). Firing event to {listeners} listeners...");
|
||||
|
||||
OnFortDefeated?.Invoke();
|
||||
|
||||
Logging.Debug($"[FortController] {fortName} - OnFortDefeated event fired");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Debug($"[FortController] {fortName} - Not defeated yet ({HpPercentage:F1}% >= {defeatThresholdPercent:F1}%)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,13 @@ namespace Minigames.FortFight.Projectiles
|
||||
public Vector2 LaunchDirection { get; protected set; }
|
||||
public float LaunchForce { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timeout
|
||||
|
||||
private const float ProjectileTimeout = 10f; // Destroy projectile after 10 seconds if stuck/off-map
|
||||
private Coroutine timeoutCoroutine;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Components
|
||||
@@ -181,6 +188,41 @@ namespace Minigames.FortFight.Projectiles
|
||||
|
||||
// Fire event
|
||||
OnLaunched?.Invoke(this);
|
||||
|
||||
// Start timeout - destroy projectile after configured time if it hasn't been destroyed
|
||||
StartTimeoutTimer();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timeout
|
||||
|
||||
/// <summary>
|
||||
/// Start timeout timer. Projectile will auto-destroy after timeout to prevent stuck/lost projectiles.
|
||||
/// </summary>
|
||||
private void StartTimeoutTimer()
|
||||
{
|
||||
if (timeoutCoroutine != null)
|
||||
{
|
||||
StopCoroutine(timeoutCoroutine);
|
||||
}
|
||||
|
||||
timeoutCoroutine = StartCoroutine(TimeoutCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timeout coroutine - destroys projectile after configured time
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator TimeoutCoroutine()
|
||||
{
|
||||
yield return new WaitForSeconds(ProjectileTimeout);
|
||||
|
||||
// Only destroy if still exists (might have been destroyed by collision already)
|
||||
if (this != null && gameObject != null)
|
||||
{
|
||||
Logging.Debug($"[ProjectileBase] {gameObject.name} timed out after {ProjectileTimeout}s, destroying...");
|
||||
DestroyProjectile();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -305,6 +347,13 @@ namespace Minigames.FortFight.Projectiles
|
||||
{
|
||||
Logging.Debug($"[ProjectileBase] Destroying {gameObject.name}");
|
||||
|
||||
// Stop timeout coroutine if running
|
||||
if (timeoutCoroutine != null)
|
||||
{
|
||||
StopCoroutine(timeoutCoroutine);
|
||||
timeoutCoroutine = null;
|
||||
}
|
||||
|
||||
// Fire destroyed event
|
||||
OnDestroyed?.Invoke(this);
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ namespace Minigames.FortFight.Projectiles
|
||||
float lifetime = GetEffectLifetime(effect);
|
||||
Destroy(effect, lifetime);
|
||||
}
|
||||
|
||||
// Destroy trash piece immediately after dealing damage
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,14 +19,10 @@ namespace Minigames.FortFight.UI
|
||||
[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;
|
||||
// Note: AmmunitionManager and TurnManager accessed via singletons
|
||||
|
||||
[Header("UI")]
|
||||
[Tooltip("Array of ammo buttons in this panel")]
|
||||
@@ -44,21 +40,11 @@ namespace Minigames.FortFight.UI
|
||||
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!");
|
||||
@@ -78,10 +64,10 @@ namespace Minigames.FortFight.UI
|
||||
// Initialize ammo buttons with player context
|
||||
InitializeButtons();
|
||||
|
||||
// Subscribe to turn events
|
||||
if (turnManager != null)
|
||||
// Subscribe to turn events via singleton
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.OnTurnStarted += HandleTurnStarted;
|
||||
TurnManager.Instance.OnTurnStarted += HandleTurnStarted;
|
||||
}
|
||||
|
||||
// Start hidden
|
||||
@@ -93,9 +79,9 @@ namespace Minigames.FortFight.UI
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Unsubscribe from events
|
||||
if (turnManager != null)
|
||||
if (TurnManager.Instance != null)
|
||||
{
|
||||
turnManager.OnTurnStarted -= HandleTurnStarted;
|
||||
TurnManager.Instance.OnTurnStarted -= HandleTurnStarted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,13 +90,13 @@ namespace Minigames.FortFight.UI
|
||||
/// </summary>
|
||||
private void InitializeButtons()
|
||||
{
|
||||
if (ammunitionManager == null || slingshotController == null || ammoButtons == null)
|
||||
if (AmmunitionManager.Instance == null || slingshotController == null || ammoButtons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get available projectile types from settings
|
||||
var availableTypes = ammunitionManager.GetAvailableProjectileTypes();
|
||||
var availableTypes = AmmunitionManager.Instance.GetAvailableProjectileTypes();
|
||||
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
|
||||
|
||||
if (settings == null)
|
||||
@@ -126,7 +112,7 @@ namespace Minigames.FortFight.UI
|
||||
var config = settings.GetProjectileConfig(availableTypes[i]);
|
||||
if (config != null)
|
||||
{
|
||||
ammoButtons[i].Initialize(config, ammunitionManager, slingshotController, playerIndex);
|
||||
ammoButtons[i].Initialize(config, AmmunitionManager.Instance, slingshotController, playerIndex);
|
||||
Logging.Debug($"[AmmunitionPanel] Player {playerIndex}: Initialized button for {config.displayName}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ namespace Minigames.FortFight.UI
|
||||
[Tooltip("Leave empty to auto-find")]
|
||||
[SerializeField] private Core.FortManager fortManager;
|
||||
|
||||
[Header("Debug Display")]
|
||||
[Tooltip("Show numerical HP values (current/max)")]
|
||||
[SerializeField] private bool debugDisplay = false;
|
||||
[Tooltip("Text field to display 'current/max' HP values")]
|
||||
[SerializeField] private TextMeshProUGUI debugHpText;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private State
|
||||
@@ -45,11 +51,17 @@ namespace Minigames.FortFight.UI
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
Logging.Debug($"[FortHealthUI] OnManagedStart - autoBindToFort: {autoBindToFort}, isPlayerFort: {isPlayerFort}");
|
||||
|
||||
// Auto-bind to dynamically spawned forts
|
||||
if (autoBindToFort)
|
||||
{
|
||||
SetupAutoBinding();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning($"[FortHealthUI] Auto-bind disabled! HP UI will not update.");
|
||||
}
|
||||
}
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
@@ -76,33 +88,53 @@ namespace Minigames.FortFight.UI
|
||||
|
||||
private void SetupAutoBinding()
|
||||
{
|
||||
// Find FortManager if not assigned
|
||||
if (fortManager == null)
|
||||
{
|
||||
fortManager = FindFirstObjectByType<Core.FortManager>();
|
||||
}
|
||||
Logging.Debug($"[FortHealthUI] SetupAutoBinding called for {(isPlayerFort ? "PLAYER" : "ENEMY")} fort");
|
||||
|
||||
// Get FortManager via singleton
|
||||
fortManager = Core.FortManager.Instance;
|
||||
|
||||
if (fortManager == null)
|
||||
{
|
||||
Logging.Warning($"[FortHealthUI] Cannot auto-bind: FortManager not found. HP UI will not update.");
|
||||
Logging.Error($"[FortHealthUI] CRITICAL: FortManager.Instance is NULL! HP UI will not work.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to appropriate spawn event
|
||||
Logging.Debug($"[FortHealthUI] FortManager found. Checking if fort already spawned...");
|
||||
|
||||
// Check if fort already spawned (missed the spawn event)
|
||||
FortController existingFort = isPlayerFort ? fortManager.PlayerFort : fortManager.EnemyFort;
|
||||
if (existingFort != null)
|
||||
{
|
||||
Logging.Debug($"[FortHealthUI] Fort already exists, binding immediately: {existingFort.FortName}");
|
||||
BindToFort(existingFort);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug($"[FortHealthUI] Fort not spawned yet. Subscribing to spawn events...");
|
||||
|
||||
// Subscribe to appropriate spawn event for future spawn
|
||||
if (isPlayerFort)
|
||||
{
|
||||
fortManager.OnPlayerFortSpawned += OnFortSpawned;
|
||||
Logging.Debug($"[FortHealthUI] Subscribed to PLAYER fort spawn");
|
||||
Logging.Debug($"[FortHealthUI] ✅ Subscribed to OnPlayerFortSpawned event");
|
||||
}
|
||||
else
|
||||
{
|
||||
fortManager.OnEnemyFortSpawned += OnFortSpawned;
|
||||
Logging.Debug($"[FortHealthUI] Subscribed to ENEMY fort spawn");
|
||||
Logging.Debug($"[FortHealthUI] ✅ Subscribed to OnEnemyFortSpawned event");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFortSpawned(FortController fort)
|
||||
{
|
||||
Logging.Debug($"[FortHealthUI] 🎯 OnFortSpawned event received! Fort: {fort?.FortName ?? "NULL"}");
|
||||
|
||||
if (fort == null)
|
||||
{
|
||||
Logging.Error($"[FortHealthUI] Fort is NULL in spawn callback!");
|
||||
return;
|
||||
}
|
||||
|
||||
BindToFort(fort);
|
||||
}
|
||||
|
||||
@@ -140,7 +172,7 @@ namespace Minigames.FortFight.UI
|
||||
|
||||
UpdateDisplay();
|
||||
|
||||
Logging.Debug($"[FortHealthUI] Bound to fort: {fort.FortName}");
|
||||
Logging.Debug($"[FortHealthUI] Bound to fort: {fort.FortName}. Event subscription successful.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -149,6 +181,7 @@ namespace Minigames.FortFight.UI
|
||||
|
||||
private void OnFortDamaged(float damage, float hpPercentage)
|
||||
{
|
||||
Logging.Debug($"[FortHealthUI] OnFortDamaged received! Damage: {damage}, HP%: {hpPercentage:F1}%, Fort: {trackedFort?.FortName}");
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
@@ -158,20 +191,52 @@ namespace Minigames.FortFight.UI
|
||||
|
||||
private void UpdateDisplay()
|
||||
{
|
||||
if (trackedFort == null) return;
|
||||
if (trackedFort == null)
|
||||
{
|
||||
Logging.Warning("[FortHealthUI] UpdateDisplay called but trackedFort is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
float hpPercent = trackedFort.HpPercentage;
|
||||
|
||||
Logging.Debug($"[FortHealthUI] UpdateDisplay - Fort: {trackedFort.FortName}, HP: {hpPercent:F1}%");
|
||||
|
||||
// Update slider
|
||||
if (hpSlider != null)
|
||||
{
|
||||
hpSlider.value = hpPercent / 100f;
|
||||
Logging.Debug($"[FortHealthUI] Slider updated to {hpSlider.value:F2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[FortHealthUI] hpSlider is null!");
|
||||
}
|
||||
|
||||
// Update text
|
||||
if (hpPercentageText != null)
|
||||
{
|
||||
hpPercentageText.text = $"{hpPercent:F0}%";
|
||||
Logging.Debug($"[FortHealthUI] Text updated to {hpPercentageText.text}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning("[FortHealthUI] hpPercentageText is null!");
|
||||
}
|
||||
|
||||
// Update debug HP display (current/max)
|
||||
if (debugHpText != null)
|
||||
{
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugHpText.gameObject.SetActive(true);
|
||||
float currentHp = trackedFort.CurrentHp;
|
||||
float maxHp = trackedFort.MaxHp;
|
||||
debugHpText.text = $"{currentHp:F0}/{maxHp:F0}";
|
||||
}
|
||||
else
|
||||
{
|
||||
debugHpText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Update color based on HP
|
||||
|
||||
234
Assets/Scripts/Minigames/FortFight/UI/GameOverUI.cs
Normal file
234
Assets/Scripts/Minigames/FortFight/UI/GameOverUI.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.FortFight.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Game Over UI - displays when a fort is defeated.
|
||||
/// Shows match time, winner, and restart button.
|
||||
/// </summary>
|
||||
public class GameOverUI : ManagedBehaviour
|
||||
{
|
||||
#region Inspector Properties
|
||||
|
||||
[Header("UI References")]
|
||||
[Tooltip("Root GameObject to show/hide the entire UI")]
|
||||
[SerializeField] private GameObject rootPanel;
|
||||
|
||||
[Tooltip("Text showing elapsed time")]
|
||||
[SerializeField] private TextMeshProUGUI elapsedTimeText;
|
||||
|
||||
[Tooltip("Text showing winner")]
|
||||
[SerializeField] private TextMeshProUGUI winnerText;
|
||||
|
||||
[Tooltip("Restart button")]
|
||||
[SerializeField] private Button restartButton;
|
||||
|
||||
[Header("Optional Visuals")]
|
||||
[Tooltip("Optional canvas group for fade-in")]
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
internal override void OnManagedAwake()
|
||||
{
|
||||
base.OnManagedAwake();
|
||||
|
||||
// Validate references
|
||||
if (rootPanel == null)
|
||||
{
|
||||
Logging.Error("[GameOverUI] Root panel not assigned!");
|
||||
}
|
||||
|
||||
if (elapsedTimeText == null)
|
||||
{
|
||||
Logging.Warning("[GameOverUI] Elapsed time text not assigned!");
|
||||
}
|
||||
|
||||
if (winnerText == null)
|
||||
{
|
||||
Logging.Warning("[GameOverUI] Winner text not assigned!");
|
||||
}
|
||||
|
||||
if (restartButton == null)
|
||||
{
|
||||
Logging.Error("[GameOverUI] Restart button not assigned!");
|
||||
}
|
||||
|
||||
// Setup button listener
|
||||
if (restartButton != null)
|
||||
{
|
||||
restartButton.onClick.AddListener(OnRestartClicked);
|
||||
}
|
||||
|
||||
// Ensure canvas group exists for fade
|
||||
if (canvasGroup == null && rootPanel != null)
|
||||
{
|
||||
canvasGroup = rootPanel.GetComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
// Start hidden
|
||||
Hide();
|
||||
}
|
||||
|
||||
internal override void OnManagedDestroy()
|
||||
{
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Remove button listener
|
||||
if (restartButton != null)
|
||||
{
|
||||
restartButton.onClick.RemoveListener(OnRestartClicked);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OnRestartClicked()
|
||||
{
|
||||
Logging.Debug("[GameOverUI] Restart button clicked, reloading scene...");
|
||||
RestartGame();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Display
|
||||
|
||||
/// <summary>
|
||||
/// Show the game over UI with match results
|
||||
/// Called by FortFightGameManager when game ends
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
if (rootPanel != null)
|
||||
{
|
||||
rootPanel.SetActive(true);
|
||||
}
|
||||
|
||||
// Get game manager for elapsed time
|
||||
var gameManager = Core.FortFightGameManager.Instance;
|
||||
if (gameManager != null)
|
||||
{
|
||||
float elapsedTime = gameManager.ElapsedGameTime;
|
||||
UpdateElapsedTime(elapsedTime);
|
||||
|
||||
// Determine winner
|
||||
DetermineWinner();
|
||||
}
|
||||
|
||||
// Optional: Fade in
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 0f;
|
||||
StartCoroutine(FadeIn());
|
||||
}
|
||||
|
||||
Logging.Debug("[GameOverUI] Game over UI shown");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide the game over UI
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
if (rootPanel != null)
|
||||
{
|
||||
rootPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the elapsed time display
|
||||
/// </summary>
|
||||
private void UpdateElapsedTime(float seconds)
|
||||
{
|
||||
if (elapsedTimeText == null) return;
|
||||
|
||||
// Format as MM:SS
|
||||
int minutes = Mathf.FloorToInt(seconds / 60f);
|
||||
int secs = Mathf.FloorToInt(seconds % 60f);
|
||||
|
||||
elapsedTimeText.text = $"{minutes:00}:{secs:00}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine and display the winner
|
||||
/// </summary>
|
||||
private void DetermineWinner()
|
||||
{
|
||||
if (winnerText == null) return;
|
||||
|
||||
var fortManager = Core.FortManager.Instance;
|
||||
if (fortManager == null) return;
|
||||
|
||||
bool playerDefeated = fortManager.PlayerFort?.IsDefeated ?? false;
|
||||
bool enemyDefeated = fortManager.EnemyFort?.IsDefeated ?? false;
|
||||
|
||||
if (playerDefeated && enemyDefeated)
|
||||
{
|
||||
winnerText.text = "DRAW!";
|
||||
}
|
||||
else if (playerDefeated)
|
||||
{
|
||||
winnerText.text = "PLAYER TWO WINS!";
|
||||
}
|
||||
else if (enemyDefeated)
|
||||
{
|
||||
winnerText.text = "PLAYER ONE WINS!";
|
||||
}
|
||||
else
|
||||
{
|
||||
winnerText.text = "GAME OVER";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fade in the UI over time
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator FadeIn()
|
||||
{
|
||||
float duration = 0.5f;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsed / duration);
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Restart
|
||||
|
||||
/// <summary>
|
||||
/// Restart the game by reloading the current scene
|
||||
/// </summary>
|
||||
private void RestartGame()
|
||||
{
|
||||
// Use Unity's SceneManager to reload current scene
|
||||
string currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
|
||||
Logging.Debug($"[GameOverUI] Reloading scene: {currentScene}");
|
||||
UnityEngine.SceneManagement.SceneManager.LoadScene(currentScene);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/Minigames/FortFight/UI/GameOverUI.cs.meta
Normal file
3
Assets/Scripts/Minigames/FortFight/UI/GameOverUI.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffb13ad5109340eba06f9c02082ece94
|
||||
timeCreated: 1764806274
|
||||
@@ -1,6 +1,5 @@
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UI.Core;
|
||||
using Minigames.FortFight.Core;
|
||||
@@ -48,8 +47,8 @@ namespace Minigames.FortFight.UI
|
||||
{
|
||||
base.OnManagedStart();
|
||||
|
||||
// Get turn manager reference
|
||||
turnManager = FindFirstObjectByType<TurnManager>();
|
||||
// Get turn manager reference via singleton
|
||||
turnManager = TurnManager.Instance;
|
||||
|
||||
if (turnManager != null)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ MonoBehaviour:
|
||||
weakPointExplosionRadius: 5
|
||||
weakPointExplosionDamage: 50
|
||||
weakPointExplosionForce: 50
|
||||
fortDefeatThreshold: 0.3
|
||||
fortDefeatThreshold: 0
|
||||
blockGravityScale: 2
|
||||
projectileGravityScale: 1
|
||||
projectileSettleDelay: 2.5
|
||||
@@ -77,13 +77,17 @@ MonoBehaviour:
|
||||
description:
|
||||
damage: 20
|
||||
mass: 5
|
||||
vacuumSlideForce: 15
|
||||
vacuumSlideSpeed: 10
|
||||
vacuumDestroyBlockCount: 2
|
||||
vacuumBlockDamage: 999
|
||||
trashBagPieceCount: 5
|
||||
trashBagPieceForce: 10
|
||||
trashBagSpreadAngle: 60
|
||||
trashPieceDamage: 5
|
||||
trashPieceLifetime: 5
|
||||
ceilingFanActivationDelay: 1
|
||||
ceilingFanDropDelay: 0.2
|
||||
ceilingFanDropSpeed: 20
|
||||
baseLaunchForce: 150
|
||||
minForceMultiplier: 0.1
|
||||
maxForceMultiplier: 1
|
||||
|
||||
Reference in New Issue
Block a user