Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / Dialogue / DialogueManager.cs
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());
            }
        }
    }
}