Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / Dialogue / DialogueManager.cs
  1. using Ink.Runtime;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using TMPro;
  5. using UnityEngine;
  6. using UnityEngine.EventSystems;
  7. using UnityEngine.UI;
  8. public class DialogueManager : MonoBehaviour, IObserver
  9. {
  10. private static DialogueManager instance;
  11. [Header("Prefab Dialogue UI Objects: ")]
  12. [SerializeField] private GameObject dialoguePanelPrefab;
  13. [SerializeField] private Button buttonPrefab;
  14. [SerializeField] private Transform canvasTransform;
  15. private VerticalLayoutGroup buttonContainer; // Container for buttons
  16. [Header("Observer Type: ")]
  17. [SerializeField] private NotificationType notificationType;
  18. private Story currentStory;
  19. private TextMeshProUGUI dialogueText;
  20. private PlayerController playerController;
  21. private bool isDisplaying = false;
  22. private GameObject dialoguePanelInstance;
  23. public Story CurrentStory => currentStory;
  24. //Properties
  25. public static DialogueManager Instance => instance;
  26. private void Awake()
  27. {
  28. if (instance != null && instance != this)
  29. {
  30. Destroy(gameObject); // Ensures there's only one instance
  31. }
  32. else
  33. {
  34. instance = this;
  35. }
  36. }
  37. // Start is called before the first frame update
  38. void Start()
  39. {
  40. //canvasTransform = GameObject.Find("Canvas").transform;
  41. playerController = PlayerController.Instance;
  42. }
  43. // Update is called once per frame
  44. void Update()
  45. {
  46. //Stops the player movement if the dialogue is active
  47. if (!isDisplaying)
  48. {
  49. playerController.CanMove = true;
  50. return;
  51. }
  52. playerController.CanMove = false;
  53. if (Input.GetKeyDown(KeyCode.Return))
  54. {
  55. //Continues the story
  56. ContinueStory();
  57. }
  58. }
  59. private IEnumerator WaitForAnimation()
  60. {
  61. InitDialogue(); // Start the dialogue once the story is set
  62. Animator anim = dialoguePanelInstance.GetComponent<Animator>();
  63. float speed = anim.GetCurrentAnimatorStateInfo(0).speed;
  64. AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
  65. float duration = clipInfo[0].clip.length / speed;
  66. yield return new WaitForSeconds(duration);
  67. ContinueStory(); // Start the story immediately
  68. }
  69. //Starts the dialogue
  70. public void InitDialogue()
  71. {
  72. dialoguePanelInstance = Instantiate(dialoguePanelPrefab, canvasTransform);
  73. buttonContainer = dialoguePanelInstance.GetComponentInChildren<VerticalLayoutGroup>();
  74. dialogueText = dialoguePanelInstance.GetComponentInChildren<TextMeshProUGUI>();
  75. isDisplaying = true;
  76. }
  77. private void ContinueStory()
  78. {
  79. if (currentStory.canContinue)
  80. {
  81. //Set text for the current dialogue line
  82. dialogueText.text = currentStory.Continue();
  83. //Display choices, if any, for the current dialogue
  84. DisplayChoices();
  85. }
  86. else
  87. {
  88. //Exits the Dialogue if doesn't have more text lines to display
  89. StartCoroutine(ExitDialogue());
  90. }
  91. }
  92. //Exits the dialogue
  93. private IEnumerator ExitDialogue()
  94. {
  95. Animator anim = dialoguePanelInstance.GetComponent<Animator>();
  96. anim.SetTrigger("Close");
  97. float speed = anim.GetCurrentAnimatorStateInfo(0).speed;
  98. AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
  99. float duration = clipInfo[0].clip.length / speed;
  100. yield return new WaitForSeconds(duration);
  101. isDisplaying = false;
  102. dialogueText = null;
  103. buttonContainer = null;
  104. currentStory = null;
  105. Destroy(dialoguePanelInstance);
  106. }
  107. //Display Choices of the current story dialogue
  108. private void DisplayChoices()
  109. {
  110. //Get the current story choices
  111. List<Choice> currentChoices = currentStory.currentChoices;
  112. List<GameObject> buttonGameObjects = new List<GameObject>();
  113. ClearChoices();
  114. //if any
  115. if (currentChoices.Count > 0)
  116. {
  117. Debug.Log($"Number of choices: {currentChoices.Count}");
  118. //Create buttons for each choice
  119. for (int i = 0; i < currentChoices.Count; i++)
  120. {
  121. Button button = Instantiate(buttonPrefab, buttonContainer.transform);
  122. button.GetComponentInChildren<TextMeshProUGUI>().text = currentChoices[i].text;
  123. buttonGameObjects.Add(button.gameObject);
  124. // Add click listener to button
  125. int choiceIndex = i; // Capture the correct index for the closure
  126. button.onClick.AddListener(() => OnChoiceSelected(choiceIndex));
  127. }
  128. StartCoroutine(SelectedFirstChoice(buttonGameObjects));
  129. }
  130. }
  131. // Clear previous dialogue choice buttons
  132. private void ClearChoices()
  133. {
  134. foreach (Transform child in buttonContainer.transform)
  135. {
  136. Destroy(child.gameObject); // Destroy each button
  137. }
  138. }
  139. // Handle when a choice is selected
  140. private void OnChoiceSelected(int choiceIndex)
  141. {
  142. currentStory.ChooseChoiceIndex(choiceIndex);
  143. ContinueStory();
  144. }
  145. private IEnumerator SelectedFirstChoice(List<GameObject> choices)
  146. {
  147. //Event System requires we clear it first, then wait
  148. //For at least one frame before we set the current selected object
  149. EventSystem.current.SetSelectedGameObject(null);
  150. yield return new WaitForEndOfFrame();
  151. EventSystem.current.SetSelectedGameObject(choices[0]);
  152. }
  153. //Notify the observer
  154. public void OnNotify(ObserverNotification notification)
  155. {
  156. //Check for the notification type
  157. if (notification.NotificationType == notificationType)
  158. {
  159. if (currentStory == null)
  160. {
  161. //Check if contains the necessary components
  162. if (notification.ContainsKey("Story"))
  163. {
  164. //Adds a story
  165. currentStory = notification.GetData<Story>("Story");
  166. //Sets the name of the NPC
  167. currentStory.variablesState["npc_name"] = notification.GetData<string>("NPCname");
  168. StartCoroutine(WaitForAnimation());
  169. }
  170. }
  171. else if(notification.ContainsKey("ExitStory"))
  172. {
  173. StartCoroutine(ExitDialogue());
  174. }
  175. }
  176. }
  177. }