- using Ink.Runtime;
- using System.Collections;
- using System.Collections.Generic;
- using TMPro;
- using UnityEngine;
- using UnityEngine.EventSystems;
- using UnityEngine.UI;
- public class DialogueManager : MonoBehaviour, IObserver
- {
- private static DialogueManager instance;
- [Header("Prefab Dialogue UI Objects: ")]
- [SerializeField] private GameObject dialoguePanelPrefab;
- [SerializeField] private Button buttonPrefab;
- [SerializeField] private Transform canvasTransform;
- private VerticalLayoutGroup buttonContainer; // Container for buttons
- [Header("Observer Type: ")]
- [SerializeField] private NotificationType notificationType;
- private Story currentStory;
- private TextMeshProUGUI dialogueText;
- private PlayerController playerController;
- private bool isDisplaying = false;
- private GameObject dialoguePanelInstance;
- public Story CurrentStory => currentStory;
- //Properties
- public static DialogueManager Instance => instance;
- private void Awake()
- {
- if (instance != null && instance != this)
- {
- Destroy(gameObject); // Ensures there's only one instance
- }
- else
- {
- instance = this;
- }
- }
- // Start is called before the first frame update
- void Start()
- {
- //canvasTransform = GameObject.Find("Canvas").transform;
- playerController = PlayerController.Instance;
- }
- // Update is called once per frame
- void Update()
- {
- //Stops the player movement if the dialogue is active
- if (!isDisplaying)
- {
- playerController.CanMove = true;
- return;
- }
- playerController.CanMove = false;
- if (Input.GetKeyDown(KeyCode.Return))
- {
- //Continues the story
- ContinueStory();
- }
-
- }
- private IEnumerator WaitForAnimation()
- {
- InitDialogue(); // Start the dialogue once the story is set
- Animator anim = dialoguePanelInstance.GetComponent<Animator>();
- float speed = anim.GetCurrentAnimatorStateInfo(0).speed;
- AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
- float duration = clipInfo[0].clip.length / speed;
- yield return new WaitForSeconds(duration);
- ContinueStory(); // Start the story immediately
- }
- //Starts the dialogue
- public void InitDialogue()
- {
- dialoguePanelInstance = Instantiate(dialoguePanelPrefab, canvasTransform);
- buttonContainer = dialoguePanelInstance.GetComponentInChildren<VerticalLayoutGroup>();
- dialogueText = dialoguePanelInstance.GetComponentInChildren<TextMeshProUGUI>();
- isDisplaying = true;
- }
- private void ContinueStory()
- {
- if (currentStory.canContinue)
- {
- //Set text for the current dialogue line
- dialogueText.text = currentStory.Continue();
- //Display choices, if any, for the current dialogue
- DisplayChoices();
- }
- else
- {
- //Exits the Dialogue if doesn't have more text lines to display
- StartCoroutine(ExitDialogue());
- }
- }
- //Exits the dialogue
- private IEnumerator ExitDialogue()
- {
- Animator anim = dialoguePanelInstance.GetComponent<Animator>();
- anim.SetTrigger("Close");
- float speed = anim.GetCurrentAnimatorStateInfo(0).speed;
- AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
- float duration = clipInfo[0].clip.length / speed;
- yield return new WaitForSeconds(duration);
- isDisplaying = false;
- dialogueText = null;
- buttonContainer = null;
- currentStory = null;
- Destroy(dialoguePanelInstance);
- }
- //Display Choices of the current story dialogue
- private void DisplayChoices()
- {
- //Get the current story choices
- List<Choice> currentChoices = currentStory.currentChoices;
- List<GameObject> buttonGameObjects = new List<GameObject>();
- ClearChoices();
- //if any
- if (currentChoices.Count > 0)
- {
- Debug.Log($"Number of choices: {currentChoices.Count}");
- //Create buttons for each choice
- for (int i = 0; i < currentChoices.Count; i++)
- {
- Button button = Instantiate(buttonPrefab, buttonContainer.transform);
- button.GetComponentInChildren<TextMeshProUGUI>().text = currentChoices[i].text;
- buttonGameObjects.Add(button.gameObject);
- // Add click listener to button
- int choiceIndex = i; // Capture the correct index for the closure
- button.onClick.AddListener(() => OnChoiceSelected(choiceIndex));
- }
- StartCoroutine(SelectedFirstChoice(buttonGameObjects));
- }
- }
- // Clear previous dialogue choice buttons
- private void ClearChoices()
- {
- foreach (Transform child in buttonContainer.transform)
- {
- Destroy(child.gameObject); // Destroy each button
- }
- }
- // Handle when a choice is selected
- private void OnChoiceSelected(int choiceIndex)
- {
- currentStory.ChooseChoiceIndex(choiceIndex);
- ContinueStory();
- }
- private IEnumerator SelectedFirstChoice(List<GameObject> choices)
- {
- //Event System requires we clear it first, then wait
- //For at least one frame before we set the current selected object
- EventSystem.current.SetSelectedGameObject(null);
- yield return new WaitForEndOfFrame();
- EventSystem.current.SetSelectedGameObject(choices[0]);
- }
- //Notify the observer
- public void OnNotify(ObserverNotification notification)
- {
- //Check for the notification type
- if (notification.NotificationType == notificationType)
- {
- if (currentStory == null)
- {
- //Check if contains the necessary components
- if (notification.ContainsKey("Story"))
- {
- //Adds a story
- currentStory = notification.GetData<Story>("Story");
- //Sets the name of the NPC
- currentStory.variablesState["npc_name"] = notification.GetData<string>("NPCname");
- StartCoroutine(WaitForAnimation());
- }
- }
- else if(notification.ContainsKey("ExitStory"))
- {
- StartCoroutine(ExitDialogue());
- }
- }
- }
- }