private void OnNextStory(DialogueItem dialogue, Subject <DialogueEvent> events, Choice choice = null,
                                 string overrideMessage = null, bool silent = false)
        {
            var story    = dialogue.story;
            var self     = false;
            var noDelay  = false;
            var noBubble = false;
            var success  = false;

            if (choice != null)
            {
                var substring = choice.text?.Substring(0, Mathf.Min(choice.text?.Length ?? 0, 8));
                AnalyticsEvent.Custom("choice", new Dictionary <string, object>()
                {
                    { "path", story.state?.currentPathString },
                    { "profile", profile.Value?.name },
                    { "text", substring },
                    { "index", choice.index }
                });
                var eventHitBuilder1 = new EventHitBuilder()
                                       .SetEventCategory("conversation")
                                       .SetEventAction("choice")
                                       .SetEventLabel(choice.text)
                                       .SetCustomDimension(0, story.state?.currentPathString)
                                       .SetCustomDimension(1, profile.Value?.name);
                GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1);

                story.ChooseChoiceIndex(choice.index);
                self    = true;
                noDelay = true;
            }

            var eventHitBuilder2 = new EventHitBuilder()
                                   .SetEventCategory("conversation")
                                   .SetEventAction("position")
                                   .SetEventLabel(story.currentText)
                                   .SetCustomDimension(0, story.state?.currentPathString)
                                   .SetCustomDimension(1, profile.Value?.name);

            GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder2);

            if (story.canContinue)
            {
                var message = story.Continue();
                success  = story.currentTags.Any(s => s.ToLower() == DialogueConversationController.success);
                noBubble = story.currentTags.Any(s => s.ToLower() == DialogueConversationController.noBubble);
                self    |= story.currentTags.Any(s => s.ToLower() == DialogueConversationController.self);
                silent  |= story.currentTags.Any(s => s.ToLower() == DialogueConversationController.silent);
                if (success)
                {
                    dialogue.succeeded = true;
                }

                // Remove starting and ending quotation marks, extraneous line returns
                message = message.Trim('\n', '"');

                if (message.StartsWith(m_StartOfSelfConversation))
                {
                    self   |= true;
                    message = message.Substring(1);
                }

                // If this is the last message of the dialogue, no delay
                if (!story.canContinue && (story.currentChoices?.Count ?? 0) == 0)
                {
                    noDelay = true;
                }

                var dialogueEvent = new DialogueEvent
                {
                    text     = overrideMessage ?? message,
                    noBubble = noBubble,
                    choices  = story.currentChoices,
                    self     = self,
                    noDelay  = noDelay,
                    success  = success,
                    silent   = silent
                };
                dialogue.lastEvent = dialogueEvent;
                events.OnNext(dialogueEvent);
            }
            else
            {
                var dialogueEvent = new DialogueEvent
                {
                    finished = true,
                    success  = dialogue.succeeded
                };
                dialogue.lastEvent = dialogueEvent;
                events.OnNext(dialogueEvent);
            }
        }
        private void OnDialogueEvent(DialogueItem dialogue, DialogueEvent data, Subject <DialogueEvent> events,
                                     Subject <DialogueEvent> dialogueContinue)
        {
            // Handle the function call for riddles here. This flag prevents also fake choices used to implement
            // the "Future<int>" that we're doing here from appearing inside the chat box.
            var story      = dialogue.story;
            var dataRiddle = data.riddle;

            if (dataRiddle != null)
            {
                dialogue.suppressChoices = true;
                m_ChatBoxController.RequestInput(res =>
                {
#if UNITY_WEBGL
                    // ReSharper disable once SpecifyStringComparison
                    var answerWasCorrect = res.Trim().ToLower() == dataRiddle.Trim().ToLower();
#else
                    var answerWasCorrect = string.Equals(res, riddle, StringComparison.CurrentCultureIgnoreCase);
#endif
                    AnalyticsEvent.Custom("riddle_input", new Dictionary <string, object>()
                    {
                        { "answer", res }
                    });

                    var eventHitBuilder1 = new EventHitBuilder()
                                           .SetEventCategory("conversation")
                                           .SetEventAction("riddle_input")
                                           .SetEventLabel(res);
                    GoogleAnalyticsV4.getInstance().LogEvent(eventHitBuilder1);

                    OnNextStory(dialogue, events,
                                story.currentChoices[answerWasCorrect ? 1 : 0], overrideMessage: res);
                });
            }
            else if (data.text != null && !data.silent)
            {
                var self = data.self;
                var text = data.text;
                // Render images if one was specified
                var spriteName = ParseSpriteName(text);
                // Retrieves all the sprites that have been referenced SOMEWHERE in the scene. See m_Sprites on this
                // instance and add the sprites to make sure the image can render in the chat.
                var sprite = spriteName == null ? null : Drawing.sprites[spriteName];

                m_ConversationController.Add(new[]
                {
                    new ChatMessage
                    {
                        noBubble = data.noBubble,
                        message  = spriteName != null ? "" : data.text, image = sprite,
                        self     = self
                    }
                });
            }

            // This is definitely no longer the first message.
            dialogue.first = false;

            // Show the choices if there are any to show AND we're not awaiting an input
            if (data.choices?.Count == 0 && dataRiddle == null)
            {
                dialogueContinue?.OnNext(data);
            }
            else if (data.choices?.Count > 0 && dataRiddle == null)
            {
                m_ChatBoxController.responses.Clear();
                if (dialogue.suppressChoices)
                {
                    dialogue.suppressChoices = false;
                }
                else
                {
                    foreach (var choice in data.choices)
                    {
                        m_ChatBoxController.responses.Add(choice.text);
                    }
                }
            }
        }