using UnityEngine; using Core.Lifecycle; using UnityEngine.Audio; namespace Minigames.BirdPooper { /// /// Central game manager for Bird Pooper minigame. /// Manages game flow, UI, obstacle spawning, and reacts to player events. /// Singleton pattern for easy access. /// public class BirdPooperGameManager : ManagedBehaviour { private static BirdPooperGameManager _instance; public static BirdPooperGameManager Instance => _instance; [Header("References")] [SerializeField] private BirdPlayerController player; [SerializeField] private ObstacleSpawner obstacleSpawner; [SerializeField] private TargetSpawner targetSpawner; [SerializeField] private TapToStartController tapToStartController; [SerializeField] private GameOverScreen gameOverScreen; [SerializeField] private GameObject poopPrefab; [Header("Game State")] private int _targetsHit; private bool _isGameOver; [Header("Input")] [Tooltip("Minimum seconds between consecutive poop spawns")] [SerializeField] private float poopCooldown = 0.5f; // timestamp of last poop spawn private float _lastPoopTime = -Mathf.Infinity; // Audio stuff public bool firstTimePlaying; public AppleAudioSource audioSource; public AudioResource poopingIntro; public AudioResource poopingOutro; internal override void OnManagedAwake() { base.OnManagedAwake(); // Set singleton instance if (_instance != null && _instance != this) { Debug.LogWarning("[BirdPooperGameManager] Multiple instances detected! Destroying duplicate."); Destroy(gameObject); return; } _instance = this; // Validate references if (player == null) { Debug.LogError("[BirdPooperGameManager] Player reference not assigned!"); } if (obstacleSpawner == null) { Debug.LogError("[BirdPooperGameManager] ObstacleSpawner reference not assigned!"); } if (targetSpawner == null) { Debug.LogWarning("[BirdPooperGameManager] TargetSpawner reference not assigned! Targets will not spawn."); } if (tapToStartController == null) { Debug.LogError("[BirdPooperGameManager] TapToStartController reference not assigned!"); } if (gameOverScreen == null) { Debug.LogError("[BirdPooperGameManager] GameOverScreen reference not assigned!"); } else { // Hide game over screen on start gameOverScreen.gameObject.SetActive(false); } if (poopPrefab == null) { Debug.LogWarning("[BirdPooperGameManager] Poop prefab not assigned!"); } } /// /// Called after scene is fully loaded and any save data is restored. /// Activates tap-to-start UI instead of starting immediately. /// internal override void OnSceneRestoreCompleted() { base.OnSceneRestoreCompleted(); Debug.Log("[BirdPooperGameManager] Scene fully loaded, activating tap-to-start..."); if (tapToStartController != null) { tapToStartController.Activate(); } else { Debug.LogError("[BirdPooperGameManager] TapToStartController missing! Starting game immediately as fallback."); BeginMinigame(); } } /// /// Central method to begin the minigame. /// Initializes player, starts spawners, and sets up game state. /// public void BeginMinigame() { if (firstTimePlaying) { audioSource.audioSource.resource = poopingIntro; audioSource.Play(0); firstTimePlaying = false; } // Initialize game state _isGameOver = false; _targetsHit = 0; // Initialize and enable player if (player != null) { player.Initialize(); player.OnPlayerDamaged.AddListener(HandlePlayerDamaged); // Make bird do initial flap so first tap feels responsive player.Flap(); Debug.Log("[BirdPooperGameManager] Player initialized and event subscribed"); } else { Debug.LogError("[BirdPooperGameManager] Cannot begin minigame - player reference missing!"); } // Start obstacle spawning if (obstacleSpawner != null) { obstacleSpawner.StartSpawning(); Debug.Log("[BirdPooperGameManager] Obstacle spawner started"); } else { Debug.LogError("[BirdPooperGameManager] Cannot begin minigame - obstacle spawner reference missing!"); } // Start target spawning if (targetSpawner != null) { targetSpawner.StartSpawning(); Debug.Log("[BirdPooperGameManager] Target spawner started"); } else { Debug.LogWarning("[BirdPooperGameManager] Target spawner reference missing - targets will not spawn"); } Debug.Log("[BirdPooperGameManager] ✅ Minigame started successfully!"); } internal override void OnManagedDestroy() { // Unsubscribe from player events if (player != null) { player.OnPlayerDamaged.RemoveListener(HandlePlayerDamaged); } // Clear singleton if (_instance == this) { _instance = null; } base.OnManagedDestroy(); } /// /// Called when player takes damage/dies. /// Shows game over screen. /// private void HandlePlayerDamaged() { if (_isGameOver) return; _isGameOver = true; Debug.Log($"[BirdPooperGameManager] Player damaged - Game Over! Targets Hit: {_targetsHit}"); audioSource.audioSource.resource = poopingOutro; audioSource.Play(0); // Stop spawning obstacles if (obstacleSpawner != null) { obstacleSpawner.StopSpawning(); } // Stop spawning targets if (targetSpawner != null) { targetSpawner.StopSpawning(); } // Show game over screen if (gameOverScreen != null) { gameOverScreen.Show(); } else { Debug.LogError("[BirdPooperGameManager] GameOverScreen reference missing!"); } } /// /// Spawns a poop projectile at the player's current position. /// Called by UI button OnClick event. /// public void SpawnPoop() { // enforce cooldown if (Time.time < _lastPoopTime + poopCooldown) return; if (_isGameOver || player == null || poopPrefab == null) return; Vector3 spawnPosition = player.transform.position; Instantiate(poopPrefab, spawnPosition, Quaternion.identity); _lastPoopTime = Time.time; Debug.Log($"[BirdPooperGameManager] Spawned poop at {spawnPosition}"); } /// /// Called when a target is successfully hit by poop (Phase 5). /// public void OnTargetHit() { if (_isGameOver) return; _targetsHit++; Debug.Log($"[BirdPooperGameManager] Target Hit! Total: {_targetsHit}"); } #region Public Accessors public bool IsGameOver => _isGameOver; public int TargetsHit => _targetsHit; public BirdPlayerController Player => player; #endregion } }