using System.Collections; using System.Collections.Generic; using UnityEngine; using Game; using Music; using Player; using UnityEngine.Events; using UnityEngine.InputSystem; namespace Game { /// /// The GameManager class manages the overall game logic, including game modes, player states, /// game events, and game-over conditions. It ensures a single instance exists and provides /// functionality for starting, updating, and ending the game. /// /// /// Manages the overall game logic, including game modes, player states, game events, and game-over conditions. /// public class GameManager : MonoBehaviour { public static GameManager Instance { get; private set; } public float time = 180f; public delegate void GameEvent(); public event GameEvent StartGameEvent; public event GameEvent EndGameEvent; public static List players = new List(); public static List playerColors = new List(); public float offset = 1f; public static bool music = true; public bool gameOver = false; public GameTimer gameTimer; public static Dictionary playerHoldTimes = new Dictionary(); public static GameMode gameMode = GameMode.freeForAll; // loads a default gamemode as a safety net public static string map = "Platformer With Headroom"; // loads a default map as a safety net public Vector2 spawnPosition; public Vector2 obstacleCourseSpawnPosition; public List hatSpawnPositions = new List(); public Canvas LeaderboardCanvas; public Canvas TimerCanvas; public GameObject hatObject; /// /// Enum representing the different game modes. /// public enum GameMode { freeForAll, keepAway, obstacleCourse } /// /// Ensures only one instance of GameManager exists. /// private void Awake() { if (Instance == null) { Instance = this; } else { Destroy(gameObject); } } /// /// Starts the game and initializes music. /// private void Start() { if (MusicManager.Instance != null) { MusicManager.Instance.StartPlaylist(); } StartGame(); } /// /// Continuously updates player hold times during the game. /// private void Update() { if (gameOver) return; if (gameMode == GameMode.keepAway) { foreach (var player in players) { float holdTime = GetPlayerHoldTime(player); UpdatePlayerHoldTime(player, holdTime); } } } /// /// Retrieves the hold time of a player. /// /// The player GameObject. /// The hold time of the player. private float GetPlayerHoldTime(GameObject player) { UseItem useItem = player.GetComponent(); if (useItem != null) { return useItem.holdTime; } return 0f; } /// /// Sets up the game based on the selected game mode. /// public void StartGame() { GameManager.playerHoldTimes.Clear(); if (GameManager.players.Count == 0) return; StartGameEvent?.Invoke(); print("Starting game with mode: " + gameMode + " and map: " + map); if (gameMode == GameMode.freeForAll) { foreach (GameObject player in players) { player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right); player.GetComponent().lives = 5; } } if (gameMode == GameMode.keepAway) { if (gameTimer != null) { gameTimer.startTime = time; gameTimer.StartTimer(); } foreach (GameObject player in players) { player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right); player.GetComponent().lives = 0; } } if (gameMode == GameMode.obstacleCourse) { foreach (GameObject player in players) { player.transform.position = spawnPosition; player.GetComponent().lives = 0; } } } /// /// Handles player deaths based on the current game mode. /// /// The player that died. public void PlayerDied(Damageable player) { UseItem useItem = player.GetComponent(); if (useItem != null) { if (gameOver == false) { useItem.DropItem(); } else { //return; } } if (gameMode == GameMode.freeForAll) { player.lives--; if (player.lives <= 0 && !gameOver) { player.gameObject.SetActive(false); if (AlivePlayers().Count <= 1) { GameOver(); } } else { RespawnPlayer(player.gameObject); } } if (gameMode == GameMode.keepAway || gameMode == GameMode.obstacleCourse) { RespawnPlayer(player.gameObject); } } /// /// Respawns a player at their designated spawn point. /// /// The player GameObject to respawn. private void RespawnPlayer(GameObject player) { RespawnOnTriggerEnter respawnScript = player.GetComponent(); if (respawnScript != null) { player.transform.position = respawnScript.spawnPoint; player.GetComponent().ResetDamage(); player.GetComponent().Respawn(); } } /// /// Ends the game and determines the winner based on the game mode. /// public void GameOver() { if (LeaderboardCanvas == null){} gameOver = true; EndGameEvent?.Invoke(); if (LeaderboardCanvas != null) { LeaderboardCanvas.gameObject.SetActive(false); } if (TimerCanvas != null) { TimerCanvas.gameObject.SetActive(false); } if (gameMode == GameMode.freeForAll) { GameObject winner = AlivePlayers()[0]; print(winner.name + " is the winner"); FindFirstObjectByType().WinScene(winner); WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1); FindFirstObjectByType().HideLifeDisplay(); } if (gameMode == GameMode.keepAway) { GameObject winner = null; float maxHoldTime = -1f; foreach (var player in GameManager.playerHoldTimes) { if (player.Value > maxHoldTime) { maxHoldTime = player.Value; winner = player.Key; } } if (winner != null) { print(winner.name + " is the winner with " + maxHoldTime + " seconds!"); var cameraMovement = FindFirstObjectByType(); if (cameraMovement != null) { cameraMovement.WinScene(winner); } WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1); var lifeDisplayManager = FindFirstObjectByType(); if (lifeDisplayManager != null) { lifeDisplayManager.HideLifeDisplay(); } StartCoroutine(MoveHatToWinner(winner)); if (hatObject != null) { hatObject.SetActive(true); hatObject.GetComponent().enabled = true; hatObject.GetComponent().bodyType = RigidbodyType2D.Dynamic; } } } if (gameMode == GameMode.obstacleCourse) { GameObject winner = ObstacleCourse.playerWon; print(winner.name + " is the winner!"); FindFirstObjectByType().WinScene(winner); WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1); FindFirstObjectByType().HideLifeDisplay(); } } /// /// Moves the hat to the winner in keep-away mode. /// /// The winning player GameObject. /// An IEnumerator for coroutine execution. private IEnumerator MoveHatToWinner(GameObject winner) { while (!winner.GetComponent().IsHoldingItem()) { hatObject.transform.position = winner.transform.position + Vector3.up * 3 / 2; winner.GetComponent().PickUpItem(hatObject); yield return null; } } /// /// Returns a list of all players that are currently alive. /// /// A list of alive player GameObjects. public List AlivePlayers() { List alivePlayers = new(); foreach (GameObject player in players) { if (player.activeInHierarchy) alivePlayers.Add(player); } return alivePlayers; } /// /// Updates the player's hold time and updates the leaderboard. /// /// The player GameObject. /// The hold time to update. public void UpdatePlayerHoldTime(GameObject player, float holdTime) { bool shouldSort = false; if (playerHoldTimes.ContainsKey(player)) { if (holdTime > playerHoldTimes[player]) { shouldSort = true; } playerHoldTimes[player] = holdTime; } else { playerHoldTimes.Add(player, holdTime); shouldSort = true; } LeaderboardManager.Instance.UpdatePlayerHoldTimeText(player, holdTime); if (shouldSort) { LeaderboardManager.Instance.UpdateLeaderboard(); } } } }