// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus). // It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE) using UnityEngine; using UnityEngine.UI; using System.Collections; using UnityEngine.EventSystems; using System.Linq; namespace Fungus { public class MenuDialog : MonoBehaviour, IMenuDialog { // Currently active Menu Dialog used to display Menu options public static IMenuDialog activeMenuDialog; [Tooltip("Automatically select the first interactable button when the menu is shown.")] [SerializeField] protected bool autoSelectFirstButton = false; protected Button[] cachedButtons; protected Slider cachedSlider; public static IMenuDialog GetMenuDialog() { if (activeMenuDialog == null) { // Use first Menu Dialog found in the scene (if any) IMenuDialog md = GameObject.FindObjectOfType<MenuDialog>(); if (md != null) { activeMenuDialog = md; } if (activeMenuDialog == null) { // Auto spawn a menu dialog object from the prefab GameObject prefab = Resources.Load<GameObject>("MenuDialog"); if (prefab != null) { GameObject go = Instantiate(prefab) as GameObject; go.SetActive(false); go.name = "MenuDialog"; activeMenuDialog = go.GetComponent<IMenuDialog>(); } } } return activeMenuDialog; } protected virtual void Awake() { Button[] optionButtons = GetComponentsInChildren<Button>(); cachedButtons = optionButtons; Slider timeoutSlider = GetComponentInChildren<Slider>(); cachedSlider = timeoutSlider; if (Application.isPlaying) { // Don't auto disable buttons in the editor Clear(); } } protected virtual void OnEnable() { // The canvas may fail to update if the menu dialog is enabled in the first game frame. // To fix this we just need to force a canvas update when the object is enabled. Canvas.ForceUpdateCanvases(); } protected virtual IEnumerator WaitForTimeout(float timeoutDuration, Block targetBlock) { float elapsedTime = 0; Slider timeoutSlider = GetComponentInChildren<Slider>(); while (elapsedTime < timeoutDuration) { if (timeoutSlider != null) { float t = 1f - elapsedTime / timeoutDuration; timeoutSlider.value = t; } elapsedTime += Time.deltaTime; yield return null; } Clear(); gameObject.SetActive(false); HideSayDialog(); if (targetBlock != null) { targetBlock.StartExecution(); } } #region IMenuDialog implementation public virtual Button[] CachedButtons { get { return cachedButtons; } } public virtual Slider CachedSlider { get { return cachedSlider; } } public virtual void SetActive(bool state) { gameObject.SetActive(state); } public virtual void Clear() { StopAllCoroutines(); Button[] optionButtons = GetComponentsInChildren<Button>(); foreach (UnityEngine.UI.Button button in optionButtons) { button.onClick.RemoveAllListeners(); } foreach (UnityEngine.UI.Button button in optionButtons) { if (button != null) { button.gameObject.SetActive(false); } } Slider timeoutSlider = GetComponentInChildren<Slider>(); if (timeoutSlider != null) { timeoutSlider.gameObject.SetActive(false); } } public virtual void HideSayDialog() { ISayDialog sayDialog = SayDialog.GetSayDialog(); if (sayDialog != null) { sayDialog.FadeWhenDone = true; } } public virtual bool AddOption(string text, bool interactable, Block targetBlock) { bool addedOption = false; foreach (Button button in cachedButtons) { if (!button.gameObject.activeSelf) { button.gameObject.SetActive(true); button.interactable = interactable; if (interactable && autoSelectFirstButton && !cachedButtons.Select((x) => x.gameObject).Contains(EventSystem.current.currentSelectedGameObject)) { EventSystem.current.SetSelectedGameObject(button.gameObject); } Text textComponent = button.GetComponentInChildren<Text>(); if (textComponent != null) { textComponent.text = text; } Block block = targetBlock; button.onClick.AddListener(delegate { EventSystem.current.SetSelectedGameObject(null); StopAllCoroutines(); // Stop timeout Clear(); HideSayDialog(); if (block != null) { #if UNITY_EDITOR // Select the new target block in the Flowchart window Flowchart flowchart = block.GetFlowchart(); flowchart.SelectedBlock = block; #endif gameObject.SetActive(false); block.StartExecution(); } }); addedOption = true; break; } } return addedOption; } public virtual void ShowTimer(float duration, Block targetBlock) { if (cachedSlider != null) { cachedSlider.gameObject.SetActive(true); gameObject.SetActive(true); StopAllCoroutines(); StartCoroutine(WaitForTimeout(duration, targetBlock)); } } public virtual bool IsActive() { return gameObject.activeInHierarchy; } public virtual int DisplayedOptionsCount { get { int count = 0; foreach (Button button in cachedButtons) { if (button.gameObject.activeSelf) { count++; } } return count; } } #endregion } }