Files
AppleHillsProduction/Assets/Scripts/Minigames/BirdPooper/BirdPooperGameManager.cs

341 lines
12 KiB
C#
Raw Normal View History

using UnityEngine;
using Core.Lifecycle;
2025-12-19 01:57:45 +01:00
using UnityEngine.Audio;
namespace Minigames.BirdPooper
{
/// <summary>
/// Central game manager for Bird Pooper minigame.
/// Manages game flow, UI, obstacle spawning, and reacts to player events.
/// Singleton pattern for easy access.
/// </summary>
public class BirdPooperGameManager : ManagedBehaviour
{
private static BirdPooperGameManager _instance;
public static BirdPooperGameManager Instance => _instance;
[Header("References")]
[SerializeField] private BirdPlayerController player;
[SerializeField] private ObstacleSpawner obstacleSpawner;
2025-11-21 11:33:49 +01:00
[SerializeField] private TargetSpawner targetSpawner;
[SerializeField] private TapToStartController tapToStartController;
[SerializeField] private GameOverScreen gameOverScreen;
2025-11-21 09:22:06 +01:00
[SerializeField] private GameObject poopPrefab;
[Header("Game State")]
private int _targetsHit;
private bool _isGameOver;
2025-12-15 02:19:09 +01:00
[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;
2025-12-19 01:57:45 +01:00
// 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!");
}
2025-11-21 11:33:49 +01:00
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);
}
2025-11-21 09:22:06 +01:00
if (poopPrefab == null)
{
Debug.LogWarning("[BirdPooperGameManager] Poop prefab not assigned!");
}
}
/// <summary>
/// Called after scene is fully loaded and any save data is restored.
/// Activates tap-to-start UI instead of starting immediately.
/// </summary>
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();
}
}
/// <summary>
/// Central method to begin the minigame.
/// Initializes player, starts spawners, and sets up game state.
/// </summary>
public void BeginMinigame()
{
2025-12-19 01:57:45 +01:00
if (firstTimePlaying)
{
audioSource.audioSource.resource = poopingIntro;
audioSource.Play(0);
firstTimePlaying = false;
}
2025-11-21 09:22:06 +01:00
// Initialize game state
_isGameOver = false;
_targetsHit = 0;
2025-11-21 09:22:06 +01:00
// 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!");
}
2025-11-21 11:33:49 +01:00
// 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");
2025-11-21 11:33:49 +01:00
}
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();
}
/// <summary>
/// Called when player takes damage/dies.
/// Pauses all game entities and starts end game sequence.
/// </summary>
private void HandlePlayerDamaged()
{
if (_isGameOver) return;
2025-11-21 09:22:06 +01:00
_isGameOver = true;
Debug.Log($"[BirdPooperGameManager] Player damaged - Game Over! Targets Hit: {_targetsHit}");
2025-12-19 01:57:45 +01:00
audioSource.audioSource.resource = poopingOutro;
audioSource.Play(0);
// Stop spawning obstacles
if (obstacleSpawner != null)
{
obstacleSpawner.StopSpawning();
}
2025-11-21 11:33:49 +01:00
// 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());
}
/// <summary>
/// Pause all existing scrolling entities in the scene.
/// </summary>
private void PauseAllScrollingEntities()
{
ScrollingEntity[] entities = FindObjectsByType<ScrollingEntity>(FindObjectsSortMode.None);
foreach (ScrollingEntity entity in entities)
{
entity.Pause();
}
Debug.Log($"[BirdPooperGameManager] Paused {entities.Length} scrolling entities");
}
/// <summary>
/// End game sequence: award boosters, wait for completion, then show game over screen.
/// </summary>
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!");
}
}
2025-11-21 09:22:06 +01:00
/// <summary>
/// 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
/// </summary>
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;
}
2025-11-21 09:22:06 +01:00
/// <summary>
/// Spawns a poop projectile at the player's current position.
/// Called by UI button OnClick event.
/// </summary>
public void SpawnPoop()
{
2025-12-15 02:19:09 +01:00
// enforce cooldown
if (Time.time < _lastPoopTime + poopCooldown)
return;
if (_isGameOver || player == null || poopPrefab == null)
2025-11-21 09:22:06 +01:00
return;
Vector3 spawnPosition = player.transform.position;
Instantiate(poopPrefab, spawnPosition, Quaternion.identity);
2025-12-15 02:19:09 +01:00
_lastPoopTime = Time.time;
2025-11-21 09:22:06 +01:00
Debug.Log($"[BirdPooperGameManager] Spawned poop at {spawnPosition}");
}
/// <summary>
/// Called when a target is successfully hit by poop (Phase 5).
/// </summary>
public void OnTargetHit()
{
if (_isGameOver) return;
2025-11-21 09:22:06 +01:00
_targetsHit++;
Debug.Log($"[BirdPooperGameManager] Target Hit! Total: {_targetsHit}");
2025-11-21 09:22:06 +01:00
}
#region Public Accessors
public bool IsGameOver => _isGameOver;
public int TargetsHit => _targetsHit;
public BirdPlayerController Player => player;
2025-11-21 09:22:06 +01:00
#endregion
}
}