/*
* author: mark joshwel, sai puay
* date: 11/8/2024
* description: game manager singleton for a single source of truth state management
*/
using System;
using StarterAssets;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using Cursor = UnityEngine.Cursor;
///
/// singleton class for managing the game state as a single source of truth
///
public class GameManager : MonoBehaviour
{
///
/// enum for available menus in the game, for use with ShowMenu()
///
public enum DisplayState
{
Game,
ScreenMainMenu,
ScreenOptionsMenu,
OverlayPauseMenu,
OverlayCompleteUnderTimeMenu,
OverlayFailedOverTimeMenu,
UnassociatedState
}
///
/// singleton pattern: define instance field for accessing the singleton elsewhere
///
public static GameManager Instance;
///
/// game object for the interaction prompt
///
[SerializeField] private GameObject guiInteractionPromptObject;
///
/// game object for the heads-up display
///
[SerializeField] private GameObject guiHudObject;
///
/// game object for the completion menu
///
[SerializeField] private GameObject guiCompletedMenuObject;
///
/// reference to the player object
///
public GameObject player;
///
/// color for the stopwatch when the player is running late
///
private readonly Color _colorRunningLateRed = new(193, 39, 45);
///
/// float to keep track of the elapsed play/run/speeder time
///
private float _elapsedRunTime;
///
/// checked when the scene is loaded to restart the game and skip the main menu
///
private bool _restarting;
///
/// the current state of the game
///
private DisplayState _state = DisplayState.UnassociatedState;
///
/// the visual element object for game ui (hud/prompts/tooltips)
///
private VisualElement _ui;
///
/// hud ui label for an interaction prompt/tooltip
///
private Label _uiLabelInteractionPrompt;
///
/// hud ui label for the speed-run stopwatch
///
private Label _uiLabelStopwatch;
///
/// property to check if the game is paused based on the current DisplayState
///
public bool Paused => _state != DisplayState.Game;
///
/// function to set doesn't destroy on load and checks for multiple instances
///
/// thrown if a Heads-Up Display game object isn't set in the properties
private void Awake()
{
// check if instance hasn't been set yet
if (Instance == null)
{
// set this instance as the singleton instance
Instance = this;
// don't destroy this instance on a scene load
DontDestroyOnLoad(gameObject);
Debug.Log("GameManager: Awake as singleton instance");
}
// check if instance is already set and it's not this instance
else if (Instance != null && Instance != this)
{
Debug.Log("GameManager: Awake as non-singleton instance, destroying self");
// destroy the new instance if it's not the singleton instance
Destroy(gameObject);
}
}
///
/// called when the game starts, sets state to the main menu
///
// /// generic exception it couldn't verify a safe state when starting the game
private void Start()
{
if (guiInteractionPromptObject == null)
throw new NullReferenceException("GameManager: guiInteractionPromptObject not set");
if (guiHudObject == null)
throw new NullReferenceException("GameManager: guiHudObject not set");
// if we're restarting, skip the main menu
if (_restarting)
{
Debug.Log("GameManager.Start: inherited _restarting, honouring it rn");
NewGame();
}
else
{
Debug.Log("GameManager.Start: setting to main menu and pausing");
SetDisplayState(DisplayState.ScreenMainMenu);
PauseGameHelper(DisplayState.ScreenMainMenu);
}
}
///
/// game run speed run stopwatch logic
///
private void Update()
{
if (Paused) return;
_elapsedRunTime += Time.deltaTime;
var displayedElapsedRunTime = _elapsedRunTime;
var stopwatchPrefix = "";
if (_elapsedRunTime > 299.9999999999f)
{
stopwatchPrefix = "-";
displayedElapsedRunTime -= 10f;
_uiLabelStopwatch.style.color = _colorRunningLateRed;
}
var minutes = displayedElapsedRunTime / 60;
var seconds = displayedElapsedRunTime % 60;
var milliseconds = displayedElapsedRunTime * 1000 % 1000;
_uiLabelStopwatch.text = $"{stopwatchPrefix}{minutes:00}:{seconds:00}.{milliseconds:000}";
}
///
/// called when the game object is enabled
///
private void OnEnable()
{
InitialiseInterfaceElements();
}
///
/// initialise ui elements used by the game[ manager]
///
/// >
private void InitialiseInterfaceElements()
{
_ui = guiInteractionPromptObject.GetComponent()?.rootVisualElement;
_uiLabelInteractionPrompt = _ui.Q