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. /// Pauses all game entities and starts end game sequence. /// 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(); } // Pause all existing scrolling entities (obstacles and targets) PauseAllScrollingEntities(); // Start the end game sequence (show boosters, then game over screen) StartCoroutine(EndGameSequence()); } /// /// Pause all existing scrolling entities in the scene. /// private void PauseAllScrollingEntities() { ScrollingEntity[] entities = FindObjectsByType(FindObjectsSortMode.None); foreach (ScrollingEntity entity in entities) { entity.Pause(); } Debug.Log($"[BirdPooperGameManager] Paused {entities.Length} scrolling entities"); } /// /// End game sequence: award boosters, wait for completion, then show game over screen. /// private System.Collections.IEnumerator EndGameSequence() { // Calculate booster reward int boosterReward = CalculateBoosterReward(_targetsHit); Debug.Log($"[BirdPooperGameManager] Targets hit: {_targetsHit}, awarding {boosterReward} booster pack(s)"); // Show booster pack reward UI and wait for completion if (boosterReward > 0) { Debug.Log("[BirdPooperGameManager] Starting booster giver sequence via GameManager"); var task = Core.GameManager.Instance.GiveBoosterPacksAsync(boosterReward); // Wait for the task to complete while (!task.IsCompleted) { yield return null; } // Check for exceptions if (task.IsFaulted) { Debug.LogWarning($"[BirdPooperGameManager] Booster pack reward failed: {task.Exception?.GetBaseException().Message}"); } else { Debug.Log("[BirdPooperGameManager] Booster giver sequence finished, proceeding to game over screen"); } } else { Debug.Log("[BirdPooperGameManager] No boosters to award, proceeding directly to game over screen"); } // Now show game over screen if (gameOverScreen != null) { gameOverScreen.Show(); } else { Debug.LogError("[BirdPooperGameManager] GameOverScreen reference missing!"); } } /// /// Calculate booster pack reward based on targets hit. /// Rules: /// - First booster pack is given for simply playing the game (participation reward) /// - Every 3 hit targets gives 1 additional booster pack /// private int CalculateBoosterReward(int targetsHit) { // Base reward: 1 booster for participation int reward = 1; // Additional reward: 1 booster per 3 targets hit reward += targetsHit / 3; return reward; } /// /// 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 } }