using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; using UWE; namespace NitroxClient.MonoBehaviours.Gui.Modals; /// /// Base class for Modal components, which are dialog boxes that appear in the middle of the screen /// public abstract class Modal { /// /// Get a Modal by its type at any time (static) /// public static Dictionary Modals = new(); /// /// Current modal that is visible on the screen /// public static Modal CurrentModal; private GameObject modalSubWindow; private TextMeshProUGUI text; // All the properties that will be overriden by new instances that inherit this class public string SubWindowName { get; init; } public string ModalText { get; set; } /// /// Makes it possible to dismiss the modal by clicking outside of the modal or pressing escape (default false). /// public bool IsAvoidable { get; init; } public bool HideNoButton { get; init; } public string YesButtonText { get; init; } public string NoButtonText { get; init; } public bool FreezeGame { get; init; } public float Transparency { get; init; } public float Height { get; init; } // Is useful for calling IngameMenu::OnDeselect() from a modal class (in Hide() for example) public bool IsAvoidableBypass = false; public Modal(string yesButtonText = "YES", bool hideNoButton = true, string noButtonText = "NO", string modalText = "", bool isAvoidable = false, bool freezeGame = false, float transparency = 0.392f, float height = 195f) { Type type = GetType(); if (Modals.ContainsKey(type)) { throw new NotSupportedException($"You cannot set two modals to have the same Type"); } SubWindowName = GetType().Name; YesButtonText = yesButtonText; HideNoButton = hideNoButton; NoButtonText = noButtonText; ModalText = modalText; IsAvoidable = isAvoidable; FreezeGame = freezeGame; Transparency = transparency; // 0.392 is the default transparency for Subnautica's modal Height = height; Log.Debug($"Registered Modal {SubWindowName} of type {type}"); Modals.Add(type, this); } /// /// Adds the Modal to the screen /// public void Show() { CoroutineHost.StartCoroutine(ShowAsync()); } public IEnumerator ShowAsync() { CurrentModal?.Hide(); CurrentModal = this; yield return ShowImplementation(); } /// /// Removes the Modal from the screen /// public void Hide() { CurrentModal = null; if (FreezeGame) { FreezeTime.End(FreezeTime.Id.Quit); } if (IsAvoidable) { IngameMenu.main.OnDeselect(); } else { IsAvoidableBypass = true; IngameMenu.main.OnDeselect(); IsAvoidableBypass = false; } } /// /// Called when this modal is deselected (only when pressing outside of the modal) /// public virtual void OnDeselect() { } /// /// This creates the modal when showing it for the first time, you can't modify it afterwards /// private void InitSubWindow() { if (!IngameMenu.main) { throw new NotSupportedException($"Cannot show ingame subwindow {SubWindowName} because the ingame window does not exist."); } if (!modalSubWindow) { GameObject derivedSubWindow = IngameMenu.main.transform.Find("QuitConfirmation").gameObject; modalSubWindow = UnityEngine.Object.Instantiate(derivedSubWindow, IngameMenu.main.transform, false); modalSubWindow.name = SubWindowName; // Styling. RectTransform main = modalSubWindow.GetComponent(); main.sizeDelta = new Vector2(700, Height); RectTransform messageTransform = modalSubWindow.FindChild("Header").GetComponent(); messageTransform.sizeDelta = new Vector2(700, Height); messageTransform.anchoredPosition = new Vector2(0, 50 - Height / 2); } modalSubWindow.GetComponent().color = Color.white.WithAlpha(Transparency); // Will happen either it's initialized or not UpdateModal(); } /// /// Update the modal with informations that may change from one Show() to another /// private void UpdateModal() { text = modalSubWindow.FindChild("Header").GetComponent(); text.text = ModalText; GameObject buttonYesObject = modalSubWindow.FindChild("ButtonYes"); GameObject buttonNoObject = modalSubWindow.FindChild("ButtonNo"); Button yesButton = buttonYesObject.GetComponent