public IEnumerator say(string index, bool isPlayer)
    {
        JSONNode dialogueSet = getProperDialogueSet(isPlayer);

        JSONNode conditon = dialogueSet [index] [CONDITION_KEY];

        if (!conditon.Equals(null))
        {
            Debug.Log("A");

            Debug.Log(conditon);
            Debug.Log(!eventHasHappened(conditon));

            if (!eventHasHappened(conditon))
            {
                Debug.Log("B");

                StartCoroutine(say(dialogueSet [index][ALTERNATE_KEY], isPlayer));
                yield break;
            }
        }

        JSONNode applyCondition = dialogueSet [index] [APPLY_CONDITION_KEY];

        if (applyCondition.Count > 0)
        {
            recordEvent(applyCondition [0], applyCondition [1].AsBool);
            Debug.Log("Applying condition " + applyCondition[0]);
        }

        string    subtitle = dialogueSet [index] [TEXT_KEY];
        AudioClip audio    = getProperVoiceSet(isPlayer)[index];

        uiDialogue.displaySubtitle(subtitle);
        audioSource.clip = audio;
        audioSource.Play();

        yield return(new WaitForSeconds(audio.length + pauseBetweenLines));

        string nextLine = dialogueSet[index][NEXT_KEY];

        string[] ids = toStringArray(dialogueSet[index][RESPONSE_KEY].AsArray);

        if (nextLine != null && nextLine.Length > 0)
        {
            StartCoroutine(say(nextLine, isPlayer));
        }
        else if (ids.Length > 1)
        {
            // Player will only ever get to make choices
            // so we know this must be a player-controlled choice
            uiDialogue.showOptions(getOptionsText(index, ids), ids, this);
        }
        else if (ids.Length == 1)
        {
            // This could be player or an NPC so just flip isPlayer and speak
            StartCoroutine(say(ids[0], !isPlayer));
        }
        else if (ids.Length == 0)
        {
            endDialogue();
        }
    }