using EasyTalk.Display; using EasyTalk.Nodes; using EasyTalk.Nodes.Common; using EasyTalk.Nodes.Core; using EasyTalk.Nodes.Variable; using EasyTalk.Settings; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Events; namespace EasyTalk.Controller { /// /// Controller for managing dialogue flow between a dialogue display and a dialogue node handler. /// public class DialogueController : DialogueListener { /// /// The node handler used to process the Dialogue. /// private NodeHandler nodeHandler; /// /// The Dialogue to use. /// [SerializeField] [Tooltip("The dialogue to run.")] protected Dialogue dialogue; /// /// The Dialogue Registry to use when initializing global variables. /// [SerializeField] [Tooltip("(OPTIONAL) The Dialogue Registry to initialze global variables from. If this is not set, global variables can still be loaded from the registry set on " + "Dialogue Settings of the Dialogue Display.")] protected DialogueRegistry dialogueRegistry; /// /// The playback type of the controller. /// [SerializeField] [Tooltip("AUTOPLAY mode will automatically cycle through lines of dialog and only stop when presenting options to the user." + "\nWAIT_FOR_COMMAND mode waits for user input to move to the next line of dialogue.")] protected PlaybackType playbackType; /// /// Determines whether the controller will attempt to register and use a Dialogue Display automatically. /// [SerializeField] [Tooltip("If unchecked (set to false), the controller will process Dialogue and send events to Dialogue Listeners, but will not automatically register a Dialogue Display. Continue() must be called on the" + "controller to progress it after lines of dialogue are displayed, and options must be handled when necessary.")] private bool useDialogueDisplay = true; /// /// The dialogue display currently being used by this controller. /// [SerializeField] [Tooltip("(OPTIONAL) The dialogue display UI to use when presenting dialogue and options to the player. If left empty, the controller " + "will attempt to find a Dialogue Display when PlayDialogue() is called.")] protected AbstractDialogueDisplay dialogueDisplay; /// /// The converstion display to use when playing dialogue. /// [SerializeField] [Tooltip("(OPTIONAL) The Conversation Display to use when PlayDialogue() is called on this controller.")] protected ConversationDisplay conversationDisplay = null; /// /// The ID of the conversation display to use when initializing a conversation on a dialogue display and no conversation display is set. /// [SerializeField] [Tooltip("(OPTIONAL) The Display ID of the conversation display to set when first initializing a conversation on the dialogue display.")] protected string conversationDisplayID = null; /// /// The audio source to use for playing lines of dialogue. /// [SerializeField] [Tooltip("(OPTIONAL) The audio source to use when playing audio clips attached to dialogue.")] protected AudioSource audioSource; /// /// A collection of DialogueListeners to call as dialogue playback occurs. /// [Tooltip("The Dialogue Controller will call the relevant methods on all Dialogue Listeners in this list as events occur during dialogue playback.")] [NonReorderable] [SerializeField] protected List dialogueListeners = new List(); /// /// An event which is triggered when dialogue playback starts. /// [Tooltip("This event is triggered when dialogue playback starts.")] [SerializeField] [HideInInspector] public UnityEvent onPlay = new UnityEvent(); /// /// An event which is triggered when dialogue playback stops. /// [Tooltip("This event is triggered when the dialogue playback stops.")] [SerializeField] [HideInInspector] public UnityEvent onStop = new UnityEvent(); /// /// Whether the dialogue is currently being played. /// protected bool isRunning = false; /// /// A coroutine for playing audio for a line of dialogue. /// private Coroutine audioRoutine = null; /// /// Whetehr null keys should be sent when no key exists on a line of dialogue. /// private bool sendNullKeys = true; /// /// A flag to keep track of whether audio is currently playing. /// private bool isAudioPlaying = false; /// /// The name of the character which dialogue is currently being shown for. /// private string currentCharacterName = null; /// private void Awake() { Init(); } /// /// Calls Update() on the node handler. /// private void Update() { if (nodeHandler != null) { nodeHandler.Update(); } } /// /// Initializes the controller to prepare it for playing dialogue. /// protected virtual void Init() { nodeHandler = new NodeHandler(this.gameObject); nodeHandler.SetListener(this); if (dialogue != null) { nodeHandler.Initialize(dialogue); } if (dialogueDisplay == null && useDialogueDisplay) { dialogueDisplay = FindDialogueDisplay(); } } /// /// Starts playback of the dialogue at the specified entry node. /// /// The name of the entry point ID where the dialogue should start from. public void PlayDialogue(string entryPointName = null) { StopAllCoroutines(); if(dialogueDisplay == null && useDialogueDisplay) { dialogueDisplay = FindDialogueDisplay(); } if (dialogueDisplay != null && useDialogueDisplay) { if (dialogueDisplay.IsPlaying) { dialogueDisplay.ExitDialogue(); } dialogueDisplay.SetActiveDialogueController(this); nodeHandler.DialogueSettings = dialogueDisplay.DialogueSettings; //If no Dialogue Registry was set on the controller, use the one from the dialogue display if (dialogueRegistry == null && dialogueDisplay.DialogueSettings != null) { dialogueRegistry = dialogueDisplay.DialogueSettings.DialogueRegistry; } } //Initialize global variables on the node handler using the current dialogue registry, if there is one. nodeHandler.InitializeGlobalVariables(dialogueRegistry); isRunning = true; nodeHandler.EnterDialogue(entryPointName); } /// /// Changes the Dialogue used by the controller. /// /// The Dialogue to use. public void ChangeDialogue(Dialogue dialogue) { this.dialogue = dialogue; nodeHandler.Initialize(dialogue); } /// /// Finds a dialogue display to use. /// private AbstractDialogueDisplay FindDialogueDisplay() { if (dialogueDisplay == null) { if(AbstractDialogueDisplay.Instance != null) { return AbstractDialogueDisplay.Instance; } else { #if UNITY_2022_3_OR_NEWER return GameObject.FindFirstObjectByType(FindObjectsInactive.Include); #else return GameObject.FindObjectOfType(true); #endif } } return dialogueDisplay; } /// /// Exits the dialogue playback immediately. /// public void ExitDialogue() { bool running = isRunning; isRunning = false; if (running != isRunning && nodeHandler != null) { nodeHandler.ForceExit(); } } /// /// Called when dialogue playback begins. This will switch conversation displays used if set to do so and call OnDialogueEntered on all Dialogue Listeners registered with the controller. /// /// The name of the entry point where the Dialogue playback began. public override void OnDialogueEntered(string entryPointName) { base.OnDialogueEntered(entryPointName); if (onPlay != null) { onPlay.Invoke(); } foreach (DialogueListener listener in dialogueListeners) { listener.OnDialogueEntered(entryPointName); } } /// /// Called when dialogue playback exits. This will stop the autoplay routine and call OnDialogueExited on all registered Dialogue Listeners. /// /// The name of the exit point where the dialogue exited. public override void OnDialogueExited(string exitPointName) { base.OnDialogueExited(exitPointName); isRunning = false; if (onStop != null) { onStop.Invoke(); } foreach (DialogueListener listener in dialogueListeners.ToList()) { listener.OnDialogueExited(exitPointName); } if (this.gameObject.activeSelf) { StartCoroutine(ExitCompleted()); } } /// /// Called at the end of OnDialogueExited(). Waits for one frame and then calls OnExitCompleted on this controller and all registered dialogue listeners. /// /// private IEnumerator ExitCompleted() { yield return new WaitForEndOfFrame(); base.OnExitCompleted(); foreach (DialogueListener listener in dialogueListeners.ToList()) { listener.OnExitCompleted(); } } /// /// Called whenever options are to be presented. This method filters out options which are set to not be displayed before sending the filtered list on to /// the OnDisplayOptions method on all registered Dialogue Listeners. /// /// The list of dialogue options to present. public override void OnDisplayOptions(List options) { base.OnDisplayOptions(options); foreach (DialogueListener listener in dialogueListeners) { listener.OnDisplayOptions(options); } } /// /// Called whenever a variable value is updated in the dialogue. This method will trigger the OnVariableUpdated method on all registered Dialogue Listeners. /// /// The name of the variable which was updated. /// The new value of the variable. public override void OnVariableUpdated(string variableName, object variableValue) { base.OnVariableUpdated(variableName, variableValue); foreach(DialogueListener listener in dialogueListeners) { listener.OnVariableUpdated(variableName, variableValue); } } /// /// Called whenever a line of dialogue is to be displayed. This method does the following: /// /// Stops the current autoplay routine if it is running. /// Switches conversation displays if the controller is set to do so, such as when the character changes or a target tag is specified on the l ine of dialogue. /// Send a key tag value to any registered Dialogue Listeners via the OnActivateKey method. /// Starts the autoplay routine or calls the OnDisplayLine method on all registered Dialogue Listeners. /// Switches the audio to play audio for the new line of dialogue. /// /// /// The line of dialogue to display. public override void OnDisplayLine(ConversationLine line) { base.OnDisplayLine(line); if ((currentCharacterName == null && line.OriginalCharacterName != null) || (currentCharacterName != null && line.OriginalCharacterName == null) || !currentCharacterName.Equals(line.OriginalCharacterName)) { CharacterChanged(line.OriginalCharacterName); } ActivateKey(line); foreach (DialogueListener listener in dialogueListeners) { listener.OnDisplayLine(line); } } /// /// Activates the key of the specified line of dialogue by sending its key value to all registered Dialogue Listeners by calling their OnActivateKey methods. /// /// The line of dialogue to activate the key of. protected virtual void ActivateKey(ConversationLine line) { if (line.Key != null || sendNullKeys) { base.OnActivateKey(line.Key); foreach(DialogueListener listener in dialogueListeners) { listener.OnActivateKey(line.Key); } } } /// /// Stops any audio currently playing and starts playing audio for the provided line of dialogue. /// /// The line of dialogue to play audio for. /// The line of dialogue which was last displayed. public void SwitchLineAudio(ConversationLine line, ConversationLine previousLine) { AudioSource source = GetAudioSource(); if (source != null) { //Stop any currently playing audio clip. if (source.isPlaying) { source.Stop(); OnAudioCompleted(previousLine, true); } //Cancel any pending coroutine that might be waiting to stop audio from playing. if (audioRoutine != null) { StopCoroutine(audioRoutine); audioRoutine = null; } //Start a new coroutine to play audio. audioRoutine = StartCoroutine(PlayAudioClip(line)); } } /// /// Plays the audio clip for the specified line of dialogue. /// /// The line of dialogue to play audio for. /// protected IEnumerator PlayAudioClip(ConversationLine line) { AudioSource source = GetAudioSource(); if (line.AudioClip != null) { source.clip = line.AudioClip; source.Play(); OnAudioStarted(line); yield return new WaitForSeconds(line.AudioClip.length); OnAudioCompleted(line, false); } } /// /// Called whenever a character change is detected. If this controller is set to switch conversation displays on character change, it will attempt to do so. Additionally, /// any Dialogue Listeners registered will have their OnCharacterChanged method triggered. /// /// The name of the character being switched to. public void CharacterChanged(string newCharacterName) { base.OnCharacterChanged(this.currentCharacterName, newCharacterName); string oldCharacterName = this.currentCharacterName; this.currentCharacterName = newCharacterName; foreach(DialogueListener listener in dialogueListeners) { listener.OnCharacterChanged(oldCharacterName, newCharacterName); } } /// /// Finds and returns an audio source on this component or its child components. If the audio source is set on the controller, it will use that audio source instead of /// searching components for an audio source. /// /// An AudioSource on this GameObject or from its children if none exists on this GameObject. public AudioSource GetAudioSource() { AudioSource source = audioSource; if (source == null) { source = GetComponentInChildren(); } if(source == null && dialogueDisplay != null) { source = dialogueDisplay.GetComponent(); } if (source == null) { #if UNITY_2022_3_OR_NEWER source = GameObject.FindFirstObjectByType(FindObjectsInactive.Exclude); #else source = GameObject.FindObjectOfType(); #endif } return source; } /// /// Returns whether audio is currently being played by the controller. /// /// Whether audio is currently being played by the controller. public bool IsAudioPlaying() { return this.isAudioPlaying; } /// /// Called whenever the last line of dialogue in a conversation node is reached. If the next node is an option node, and options are set to be presented /// automatically, then the DisplayOptionsAfterDelay coroutine will be started to display options based on the delay settings. THis method also calls OnConversationEnding on all /// registered Dialogue Listeners. /// /// The current line of dialogue being displayed. /// The next node to move to after the current conversation node. public override void OnConversationEnding(ConversationLine currentLine, Node nextNode) { base.OnConversationEnding(currentLine, nextNode); foreach(DialogueListener listener in dialogueListeners) { listener.OnConversationEnding(currentLine, nextNode); } } /// /// Tells the node handler to jump to the specified option node after a certain period of time. /// /// The delay, in seconds. /// The option node to go to. /// public virtual IEnumerator GoToOptionsAfterDelay(float delay, OptionNode optionNode) { yield return nodeHandler.JumpToNodeAfterDelay(delay, optionNode); } /// /// Makes the dialogue continue forward from the current point. This method will also /// trigger the OnContinue method on all registered Dialogue Listeners. /// /// Note that a continuation is only allowed when the current node is a converation, story, or wait node. /// public void Continue() { if (!isRunning) { return; } base.OnContinue(); foreach(DialogueListener listener in dialogueListeners) { listener.OnContinue(); } nodeHandler.Continue(); } /// /// Called whenever audio playback starts for a line of dialogue. This will call the OnAudioStarted method of all registered Dialogue Listeners. /// /// The line of dialogue audio started for. public override void OnAudioStarted(ConversationLine line) { base.OnAudioStarted(line); isAudioPlaying = true; foreach(DialogueListener listener in dialogueListeners) { listener.OnAudioStarted(line); } } /// /// Called whenever audio playback finishes or is interrupted for a line of dialogue. This will call the OnAudioCompleted method of all registered Dialogue Listeners. /// /// The line of dialogue which audio ended for. /// Whether the audio was forcibly interrupted. public override void OnAudioCompleted(ConversationLine line, bool forced) { base.OnAudioCompleted(line, forced); isAudioPlaying = false; foreach(DialogueListener listener in dialogueListeners) { listener.OnAudioCompleted(line, forced); } } /// /// Called whenever the current node being processed changes. /// /// The new node. public override void OnNodeChanged(Node newNode) { base.OnNodeChanged(newNode); foreach(DialogueListener listener in dialogueListeners) { listener.OnNodeChanged(newNode); } } /// /// Chooses the specified option and calls the OnOptionChosen method of all registered Dialogue Listeners. /// /// The option to choose. public void ChooseOption(DialogueOption option) { if (!isRunning) { return; } base.OnOptionChosen(option); foreach (DialogueListener listener in dialogueListeners) { listener.OnOptionChosen(option); } nodeHandler.ChooseOption(option.OptionIndex); } /// /// Creates a coroutine to wait for the specified duration before continuing. This method also calls the Wait method on all registered Dialogue Listeners. /// /// The amount of time to wait, in seconds. public override void Wait(float timeInSeconds) { base.Wait(timeInSeconds); foreach (DialogueListener listener in dialogueListeners) { listener.Wait(timeInSeconds); } StartCoroutine(WaitAsync(timeInSeconds)); } /// /// Waits for the specified duration before continuing. /// /// The amount of time to wait, in seconds. /// private IEnumerator WaitAsync(float timeInSeconds) { yield return new WaitForSeconds(timeInSeconds); nodeHandler.Continue(); } /// public override void OnPause(string signal) { base.OnPause(signal); foreach (DialogueListener listener in dialogueListeners) { listener.OnPause(signal); } } /// public override void OnWaitingForNodeEvaluation(Node asyncNode) { base.OnWaitingForNodeEvaluation(asyncNode); foreach(DialogueListener listener in dialogueListeners) { listener.OnWaitingForNodeEvaluation(asyncNode); } } /// public override void OnExecuteAsyncNode(AsyncNode node) { base.OnExecuteAsyncNode(node); foreach (DialogueListener listener in dialogueListeners) { listener.OnExecuteAsyncNode(node); } } /// public override void OnAppendText(string text) { base.OnAppendText(text); foreach (DialogueListener listener in dialogueListeners) { listener.OnAppendText(text); } } /// public override void OnNodeEvaluationCompleted(Node asyncNode) { base.OnNodeEvaluationCompleted(asyncNode); foreach (DialogueListener listener in dialogueListeners) { listener.OnNodeEvaluationCompleted(asyncNode); } } /* public override void OnAIPromptStarted() { base.OnAIPromptStarted(); foreach (DialogueListener listener in dialogueListeners) { listener.OnAIPromptStarted(); } } public override void OnAIPromptFinished() { base.OnAIPromptFinished(); foreach (DialogueListener listener in dialogueListeners) { listener.OnAIPromptFinished(); } } public override void OnConversationClear() { base.OnConversationClear(); foreach (DialogueListener listener in dialogueListeners) { listener.OnConversationClear(); } }*/ /// /// Returns the node handler used by this controller. /// /// The node handler being used. public NodeHandler GetNodeHandler() { return nodeHandler; } /// /// Returns the conversation display to use when entering dialogue playback via this controller. /// /// The conversation display to use. public ConversationDisplay GetConversationDisplay() { return conversationDisplay; } /// /// Returns the Display ID of the conversation display to use when entering dialogue playback via this controller. /// /// public string GetConversationDisplayID() { if(conversationDisplay != null) { return conversationDisplay.DisplayID; } return conversationDisplayID; } /// /// Sets the value of the specified variable. /// /// The name of the variable. /// The value to set. public void SetStringVariable(string variableName, string value) { SetVariableValue(variableName, value, typeof(string)); } /// /// Returns the string value of the specified variable. /// /// The name of the variable to retrieve the value of. /// The string value of the specified variable. public string GetStringVariable(string variableName) { return (string)GetVariableValue(variableName, typeof(string)); } /// /// Sets the value of the specified variable. /// /// The name of the variable. /// The value to set. public void SetBoolVariable(string variableName, bool value) { SetVariableValue(variableName, value, typeof(bool)); } /// /// Returns the bool value of the specified variable. /// /// The name of the variable to retrieve the value of. /// The bool value of the specified variable. public bool GetBoolVariable(string variableName) { return (bool)GetVariableValue(variableName, typeof(bool)); } /// /// Sets the value of the specified variable. /// /// The name of the variable. /// The value to set. public void SetIntVariable(string variableName, int value) { SetVariableValue(variableName, value, typeof(int)); } /// /// Returns the int value of the specified variable. /// /// The name of the variable to retrieve the value of. /// The int value of the specified variable. public int GetIntVariable(string variableName) { return (int)GetVariableValue(variableName, typeof(int)); } /// /// Sets the value of the specified variable. /// /// The name of the variable. /// The value to set. public void SetFloatVariable(string variableName, float value) { SetVariableValue(variableName, value, typeof(float)); } /// /// Returns the float value of the specified variable. /// /// The name of the variable to retrieve the value of. /// The float value of the specified variable. public float GetFloatVariable(string variableName) { return (float)GetVariableValue(variableName, typeof(float)); } /// /// Sets the value of the specified variable. /// /// The name of the variable. /// The value to set. /// The type of the variable. /// private void SetVariableValue(string variableName, object value, Type valueType) { NodeVariable variable = nodeHandler.GetVariable(variableName); if (variable != null) { if (variable.variableType == valueType) { nodeHandler.SetVariableValue(variableName, value); } else { throw new Exception("You are attempting to set a " + valueType + " value on a " + variable.variableType + " variable. Value will not be set on '" + variableName + "'."); } } else { throw new Exception("Variable '" + variableName + "' does not exist."); } } /// /// Returns the value of the specified variable. /// /// The name of the variable. /// The variable type. /// The value of the specified variable. /// private object GetVariableValue(string variableName, Type variableType) { NodeVariable variable = nodeHandler.GetVariable(variableName); if (variable != null) { if (variable.variableType == variableType) { return variable.currentValue; } throw new Exception("You are requesting a " + variableType + " value from a " + variable.variableType + " variable. Cannot retrieve value from '" + variableName + "'."); } else { throw new Exception("Variable '" + variableName + "' does not exist."); } } /// /// Adds the specified Dialogue Listener to the controller's list of listeners. /// /// The Dialogue Listener to add. public void AddDialogueListener(DialogueListener dialogueListener) { if (!dialogueListeners.Contains(dialogueListener)) { dialogueListeners.Add(dialogueListener); } } /// /// Removes the specified Dialogue Listener from the controller's list of listeners. /// /// The Dialogue Listener to remove. public void RemoveDialogueListener(DialogueListener dialogueListener) { if (dialogueListeners.Contains(dialogueListener)) { dialogueListeners.Remove(dialogueListener); } } /// /// Removes all Dialogue Listeners from the controller. /// public void RemoveDialogueListeners() { dialogueListeners.Clear(); } /// /// Gets the playback type of this controller. /// /// The playback type. public PlaybackType GetPlaybackType() { return this.playbackType; } /// /// Sets the playback type of this controller. /// /// The playback type to use. public void SetPlaybackType(PlaybackType playbackType) { this.playbackType = playbackType; } /// /// Gets or sets the Dialogue Display currently used by this controller. /// public AbstractDialogueDisplay DialogueDisplay { get { return this.dialogueDisplay; } set { this.dialogueDisplay = value; } } /// /// Saves the values of all variables in the Dialogue to either a file, or to the PlayerPrefs. /// /// The prefix to use when saving. This prefix is appended to the beginning of the dialogue name. /// Whether the variable states should be saved to PlayerPrefs. If set to false, the variable states will be saved to /// a JSON file instead. public void SaveVariableValues(string prefix = "", bool saveToPlayerPrefs = false) { nodeHandler.SaveVariableValues(prefix, saveToPlayerPrefs); } /// /// Saves all currently loaded global variable values to PlayerPrefs or a JSON file. /// /// The prefix to append to the beginning of the PlayerPrefs entry of JSON file name. /// Whether the values should be saved to PlayerPrefs rather than a JSON file. public void SaveGlobalVariableValues(string prefix = "", bool saveToPlayerPrefs = false) { nodeHandler.SaveGlobalVariableValues(prefix, saveToPlayerPrefs); } /// /// Loads the states of the Dialogue's variables from a save if available. /// /// The prefix to use when loading. This prefix is appended to the beginning of the dialogue name. /// If true, variable states will be loaded from PlayerPrefs rather than a JSON file. public void LoadVariableValues(string prefix = "", bool loadFromPlayerPrefs = false) { nodeHandler.LoadVariableValues(prefix, loadFromPlayerPrefs); } /// /// Loads global variable values from PlayerPrefs or a JSON file. /// /// The prefix to append to the beginning of the PlayerPrefs entry or JSON file name. /// Whether the variable values should be loaded from PlayerPrefs rather than a JSON file. public void LoadGlobalVariableValues(string prefix = "", bool loadFromPlayerPrefs = false) { //Attempt to initialize the global variables from a dialogue registry prior to loading values InitializeGlobalVariables(dialogueRegistry); //Load the global variable values, if possible. Note: this will fail to load anything if no initialization //occured (for example, if the registry was null and one couldn't be found). nodeHandler.LoadGlobalVariableValues(prefix, loadFromPlayerPrefs); } /// /// Initializes global variable values from the Dialogue Registry provided. If the registry is null, an attempt will be made to load a registry from the /// active Dialogue Settings set on the node handler, and then will attempt to locate a registry on a Dialogue Display's Dialogue Settings from the scene's Hierarchy. /// The Dialogue Registry to initialize global variables from. /// The d public void InitializeGlobalVariables(DialogueRegistry registry = null) { if (registry == null) { registry = dialogueRegistry; } if (nodeHandler != null) { nodeHandler.InitializeGlobalVariables(registry); } } /// /// Defines dialogue playback types used by dialogue controllers. /// public enum PlaybackType { AUTOPLAY, WAIT_FOR_COMMAND } /// /// Sets the playback mode of the controller. If the passed in string is "auto" or "autoplay", the mode will be set to AUTOPLAY; otherwise it will be set to WAIT_FOR_COMMAND mode. /// /// A string to the playback type to use. public void SetPlaybackType(string playbackType) { if (playbackType.ToLower().Equals("auto") || playbackType.ToLower().Equals("autoplay")) { this.playbackType = PlaybackType.AUTOPLAY; } else { this.playbackType = PlaybackType.WAIT_FOR_COMMAND; } } /// /// Changes the playback mode of the controller to whichever mode it isn't currently in. If the controller is in WAIT_FOR_COMMAND mode, calling this method will /// switch it to AUTOPLAY mode, and vice versa. /// public void ChangePlaybackType() { if (this.playbackType == PlaybackType.WAIT_FOR_COMMAND) { this.playbackType = PlaybackType.AUTOPLAY; } else { this.playbackType = PlaybackType.WAIT_FOR_COMMAND; } } /// /// Gets the Dialogue currently set on the dialogue controller. /// public Dialogue CurrentDialogue { get { return this.dialogue; } } } }