Files
modular-vr/Assets/Engine/Runtime/UI/GameControl.cs

222 lines
7.0 KiB
C#

using System.Collections.Generic;
using EscapeRoomEngine.Engine.Runtime.Measurements;
using EscapeRoomEngine.Engine.Runtime.Modules.Description;
using EscapeRoomEngine.Engine.Runtime.UI.Elements;
using EscapeRoomEngine.Engine.Runtime.Utilities;
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.UI;
namespace EscapeRoomEngine.Engine.Runtime.UI
{
public enum GameState
{
Stopped, Paused, Running
}
/// <summary>
/// The component that manages the gamemaster UI and the time.
/// </summary>
public class GameControl : MonoBehaviour
{
/// <summary>
/// The active instance of the game control.
/// </summary>
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<PuzzleModuleDescription> PlannedPuzzles
{
set => puzzlePlan.Puzzles = value;
}
/// <summary>
/// The time that elapsed since the game was started.
/// </summary>
public float TimeElapsed { get; private set; }
/// <summary>
/// The time the player has spent in this room.
/// </summary>
public float TimeInRoom { get; set; }
/// <summary>
/// The target time set by the game master.
/// </summary>
public float TargetTime { get; private set; }
/// <summary>
/// The estimated total time the player will have spent when they finish this room.
/// </summary>
public float EstimatedTimeRoom { get; private set; }
/// <summary>
/// The estimated total time the player will spend in the experience.
/// </summary>
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<PauseButton>().Paused = true;
break;
case GameState.Paused:
gameState = GameState.Running;
pauseButton.GetComponent<PauseButton>().Paused = false;
break;
}
}
/// <summary>
/// Change the allowed time by a specified amount of seconds.
/// </summary>
/// <param name="seconds">The amount of seconds that will be added to the time. Can be negative to remove time.</param>
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
}
}
}