using System.Collections.Generic; using EscapeRoomEngine.Engine.Runtime.Measurements; using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Utilities; using NaughtyAttributes; using UnityEngine; using UnityEngine.UI; namespace EscapeRoomEngine.Engine.Runtime.UI { public enum GameState { Stopped, Paused, Running } /// /// The component that manages the gamemaster UI and the time. /// public class GameControl : MonoBehaviour { /// /// The active instance of the game control. /// public static GameControl Instance { get; private set; } [Tooltip("How much time in seconds should be between UI updates.")] [SerializeField] private float uiUpdateInterval = 1; [Tooltip("How much time in seconds should be between puzzle plan updates.")] [SerializeField] private float planUpdateInterval = 1; [BoxGroup("Internal")] [SerializeField] private Button startButton, stopButton, pauseButton, addTimeButton, removeTimeButton; [BoxGroup("Internal")] [SerializeField] private Text timeText, roomTimeText, estimateTimeText, targetTimeText, percentileText; [BoxGroup("Internal")] [SerializeField] private PuzzlePlan puzzlePlan; [HideInInspector] public GameState gameState = GameState.Stopped; public PuzzleModuleDescription CurrentPuzzle { set => puzzlePlan.CurrentPuzzle = value; } public List PlannedPuzzles { set => puzzlePlan.Puzzles = value; } /// /// The time that elapsed since the game was started. /// public float TimeElapsed { get; private set; } /// /// The time the player has spent in this room. /// public float TimeInRoom { get; set; } /// /// The target time set by the game master. /// public float TargetTime { get; private set; } /// /// The estimated total time the player will have spent when they finish this room. /// public float EstimatedTimeRoom { get; private set; } /// /// The estimated total time the player will spend in the experience. /// public float EstimatedTime { get; private set; } private float _previousUIUpdate, _previousPlanUpdate; private void Awake() { Instance = this; } private void Start() { TargetTime = Engine.Instance.initialTargetTime; SetTimeText(); } private void Update() { // update time if (gameState == GameState.Running) { TimeElapsed += Time.deltaTime; TimeInRoom += Time.deltaTime; } // update ui if (Time.time > _previousUIUpdate + uiUpdateInterval) { _previousUIUpdate = Time.time; SetTimeText(); UpdateStats(); } // update plan if (Time.time > _previousPlanUpdate + planUpdateInterval) { _previousPlanUpdate = Time.time; Engine.Instance.PlanPuzzles(); } // enable or disable buttons startButton.interactable = gameState == GameState.Stopped; stopButton.interactable = gameState != GameState.Stopped; pauseButton.interactable = gameState is GameState.Running or GameState.Paused; addTimeButton.interactable = gameState != GameState.Stopped; removeTimeButton.interactable = gameState != GameState.Stopped && TargetTime >= TimeElapsed + 60; } #region Time Controls public void StartGame() { gameState = GameState.Running; TimeElapsed = 0; // generate the first room if it hasn't been generated yet Engine.Instance.CurrentRoom.Match(none: () => Engine.Instance.GenerateRoom()); // start a new session Measure.StartSession(); } public void StopGame() { if (gameState != GameState.Stopped) { // was running Measure.EndSession(TimeElapsed); } gameState = GameState.Stopped; } public void PauseGame() { // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (gameState) { case GameState.Running: gameState = GameState.Paused; pauseButton.GetComponent().Paused = true; break; case GameState.Paused: gameState = GameState.Running; pauseButton.GetComponent().Paused = false; break; } } /// /// Change the allowed time by a specified amount of seconds. /// /// The amount of seconds that will be added to the time. Can be negative to remove time. public void ChangeTime(int seconds) { if (TargetTime + seconds >= TimeElapsed) { TargetTime += seconds; } } private void SetTimeText() { timeText.text = TimeToText(TimeElapsed); roomTimeText.text = TimeToText(TimeInRoom); targetTimeText.text = TimeToText(TargetTime); Engine.Instance.CurrentRoom.Match(some: room => { EstimatedTimeRoom = TimeElapsed - TimeInRoom + Mathf.Max(TimeInRoom, Measure.EstimateTime(room)); EstimatedTime = EstimatedTimeRoom + Engine.Instance.EstimatedTimeRemaining; estimateTimeText.text = TimeToText(EstimatedTime); }); } private void UpdateStats() { percentileText.text = PercentageToText(Measure.SessionPercentile()); } private static string TimeToText(float time) => $"{time.ToTimeSpan():mm':'ss}"; private static string PercentageToText(float percentage) => $"{percentage:P1}"; #endregion #region Measurements public void StoreMeasurements(Toggle toggle) { Measure.Store = toggle.isOn; } #endregion public void ExitGame() { StopGame(); #if UNITY_STANDALONE Application.Quit(); #endif #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #endif } } }