private IEnumerator speakHint(LocalizedString pString)
    {
        _speechBoxButton.onClick.RemoveAllListeners();

        float delay = 1.0f / 50;

        TextMeshProUGUI hintText = _hintLocalizedStringEvent.GetComponent <TextMeshProUGUI>();

        _hintLocalizedStringEvent.StringReference = pString;
        //Disable the localization component until finished
        _hintLocalizedStringEvent.enabled = false;
        hintText.text = "";

        string targetText = pString.GetLocalizedString();

        string[] speechAndTags = targetText.Split(new char[2] {
            '<', '>'
        });
        for (int i = 0; i < speechAndTags.Length; i++)
        {
            string section = speechAndTags[i];
            bool   isTag   = (i & 1) != 0;

            if (isTag)
            {
                string           currentText   = hintText.text;
                EncapsulatedText encapsulation = new EncapsulatedText(string.Format("<{0}>", section), speechAndTags, i);
                while (!encapsulation.IsDone)
                {
                    bool hasStepped = encapsulation.Step();
                    hintText.text = currentText + encapsulation.DisplayText;
                    if (hasStepped)
                    {
                        yield return(new WaitForSeconds(delay));
                    }
                }
                i = encapsulation.SpeechAndTagsProgress + 1;
            }
            else
            {
                for (int j = 0; j < section.Length; j++)
                {
                    hintText.text += section[j];
                    yield return(new WaitForSeconds(delay));
                }
            }
        }

        finishSpeakHint(pString);
    }
    private IEnumerator speak(Line pLine)
    {
        //Disable player interaction
        _speechBoxButton.onClick.RemoveAllListeners();
        //Calculate the delay between each letter
        float delay = 1.0f / pLine.TextCharactersPerSecond;
        //Store the text component
        TextMeshProUGUI speechText = _speechLocalizedStingEvent.GetComponent <TextMeshProUGUI>();

        _speechLocalizedStingEvent.StringReference = pLine.LineString;
        //Disable the localization component until finished
        _speechLocalizedStingEvent.enabled = false;

        //Clear the text from the text component
        speechText.text = "";

        //Handle SFX before line text
        yield return(handleSFX(pLine.SFXBeforeLine, _speechBoxButton));

        #region HandleText
        //Store the final text seperated by "<size=0>|</size>" to handle concatanation
        string[] targetText = (pLine.LineString != null) ? pLine.LineString.GetLocalizedString().Split(new string[] { "<size=0>|</size>" }, StringSplitOptions.None) : new string[1] {
            ""
        };

        //Allow the player to quickly complete text generation
        bool shouldQuickComplete = false;
        //Store the quickComplete method, so it can be enabled/disabled at anytime
        UnityAction quickCompleteAction = () => quickComplete();
        for (int i = 0; i < targetText.Length; i++)
        {
            //Allow the player to quickComplete
            _speechBoxButton.onClick.AddListener(quickCompleteAction);
            //Check for encapsulation
            string   currentText  = "";
            string[] textWithTags = targetText[i].Split(new char[2] {
                '<', '>'
            });
            for (int k = 0; k < textWithTags.Length; k++)
            {
                //Every odd element in the array is a tag
                bool isTag = (k & 1) != 0;
                if (isTag)
                {
                    //Store currently displayed text
                    currentText = speechText.text;
                    //Handle encapsulation
                    EncapsulatedText encapsulation = new EncapsulatedText(string.Format("<{0}>", textWithTags[k]), textWithTags, k);
                    while (!encapsulation.IsDone)
                    {
                        //Step through the encapsulation
                        bool hasStepped = encapsulation.Step();
                        //Append the displayed text with the encapsulated text
                        speechText.text = currentText + encapsulation.DisplayText;
                        if (hasStepped && !shouldQuickComplete)
                        {
                            yield return(new WaitForSeconds(delay));
                        }
                    }
                    //Go to the element after encapsulation
                    k = encapsulation.SpeechAndTagsProgress + 1;
                }
                else
                {
                    for (int j = 0; j < textWithTags[k].Length; j++)
                    {
                        speechText.text += textWithTags[k][j];
                        if (!shouldQuickComplete)
                        {
                            yield return(new WaitForSeconds(delay));
                        }
                    }
                }
            }
            _speechBoxButton.onClick.RemoveListener(quickCompleteAction);
            shouldQuickComplete = false;

            //Only wait for player interaction if concatanation is needed
            if (targetText.Length > 1 && i < targetText.Length - 1)
            {
                bool hasClicked = false;
                //Store the playerClicked method, so it can be enabled/disabled at anytime
                UnityAction playerClickedAction = () => playerClicked();
                _speechBoxButton.onClick.AddListener(playerClickedAction);
                _clickToContinue.SetActive(true);

                //Wait until player clicks
                while (!hasClicked)
                {
                    yield return(new WaitForEndOfFrame());
                }

                _speechBoxButton.onClick.RemoveListener(playerClickedAction);
                _clickToContinue.SetActive(false);

                #region Local Methods
                void playerClicked()
                {
                    hasClicked = true;
                }

                #endregion
            }
        }
        #endregion

        //Handle SFX after line text
        yield return(handleSFX(pLine.SFXAfterLine, _speechBoxButton));

        finishSpeak();

        #region Local Methods
        void quickComplete()
        {
            shouldQuickComplete = true;
        }

        #endregion
    }