using EasyTalk.Controller; using EasyTalk.Localization; using EasyTalk.Nodes.Common; using EasyTalk.Nodes.Core; using EasyTalk.Settings; using EasyTalk.Utils; using System.Collections.Generic; #if TEXTMESHPRO_INSTALLED using TMPro; #endif using UnityEngine; using UnityEngine.Events; namespace EasyTalk.Display { /// /// This class is an abstract framework laying the foundation for a Dialogue Display. /// public abstract class AbstractDialogueDisplay : DialogueListener { /// /// The EasyTalk dialogue settings to use. /// [Tooltip("The EasyTalk dialogue settings to use.")] [SerializeField] protected EasyTalkDialogueSettings dialogueSettings; /// /// The currently active dialogue controller. /// protected DialogueController dialogueController; /// /// An instance of a Dialogue Display. /// protected static AbstractDialogueDisplay instance; /// /// When true, the dialogue display will be destroyed when a new scene is loaded. /// [Tooltip("If set to true, the dialogue display will be destroyed when a new scene is loaded.")] [SerializeField] protected bool destroyOnLoad = false; /// /// When set to true, the dialogue display allows an immediate exit from the currently running dialogue. /// [Tooltip("When set to true, the player can exit a playing dialogue by pressing a button corresponding to the input for the Exit Conversation Action.")] [SerializeField] private bool allowQuickExit; /// /// A collection of Dialogue Listeners to call as dialogue playback occurs. /// [Tooltip("The Dialogue Display 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(); /// /// A Unity Event which is triggered whenever continuation of the current dialogue is allowed. /// [Tooltip("This event is riggered whenever continuation to the next line of dialogue is allowed.")] [SerializeField] [HideInInspector] public UnityEvent onContinueEnabled; /// /// A Unity Event which is triggered whenever continuation of the current dialogue is disabled. /// [Tooltip("This event is triggered whenever continuation to the next line of dialogue is disabled.")] [SerializeField] [HideInInspector] public UnityEvent onContinueDisabled; /// /// A Unity Event which is triggered whenever option selection is enabled (after options are presented). /// [Tooltip("This event is triggered whenever the player is allowed to select options (this normally occurs after options are displayed to the player).")] [SerializeField] [HideInInspector] public UnityEvent onOptionSelectionEnabled; /// /// A Unity Event which is triggered whenever option selection is disabled (after an option is chosen). /// [Tooltip("This event is triggered whenever option selection is disallowed (this normally occurs after an option is chosen).")] [SerializeField] [HideInInspector] public UnityEvent onOptionSelectionDisabled; /// /// A flag indicating whether the display is currently playing a dialogue. /// public bool IsPlaying { get; private set; } = false; /// /// A flag indicating whether the dialogue is currently displaying a conversation (rather than options). /// public bool IsCurrentlyInConversation { get; protected set; } = false; /// /// A flag indicating whether continuation is currently permitted in the conversation. /// private bool isContinueAllowed = false; /// /// A flag indicating whether option selection is currently permitted. /// private bool isOptionSelectionAllowed = false; /// private void Awake() { if (instance == null) { instance = this; } if(!destroyOnLoad) { DontDestroyOnLoad(this.gameObject); } SetupUtils.SetUpTextComponents(this.gameObject, false); UpdateFontsForLanguage(this); } /// /// Enables and disables Unity standard Text components and TextMeshPro components based on whether TextMeshPro is enabled/installed. /// private void OnValidate() { SetupUtils.SetUpTextComponents(this.gameObject, false); } /// /// If using the new input system, Init() sets up the input actions. This method also registers a method with the onLanguageChanged delegate so that the dialogue /// display can update fonts of all child components whenever the language is changed. /// protected virtual void Init() { EasyTalkGameState.Instance.onLanguageChanged += LanguageChanged; } /// /// Unregister the LanguageChanged method frm the onLanguageChanged delegate so that the EasyTalkGameState instance will no longer try to call this object. /// private void OnDestroy() { EasyTalkGameState.Instance.onLanguageChanged -= LanguageChanged; } /// /// If continuation is currently permitted, this will call the dialogue controller's Continue() method to continue to the next part of the current converstaion /// and disable continuation. /// public virtual void Continue() { if (isContinueAllowed && dialogueController != null) { DisableContinue(); dialogueController.Continue(); } } /// /// Selects the next option in the option display (only applicable when options are being presented). /// /// Returns true if an option was selected; false otherwise. public abstract bool SelectNextOption(); /// /// Selects the previous option in the option display (only applicable when options are being presented). /// /// Returns true if an option was selected; false otherwise. public abstract bool SelectPreviousOption(); /// /// Chooses the selected option. This method should notify the Dialogue Controller of the option that was chosen using DialogueOption.OptionIndex. /// public abstract void ChooseSelectedOption(); /// /// Selects the option which most closely corresponds to the specified direction. /// /// The direction to select an option in. /// Returns true if an option was selected; false otherwise. public abstract bool SelectOptionInDirection(Vector2 direction); /// /// This method tells the active dialogue controller to exit the dialogue immediately. /// public virtual void ExitDialogue() { if (dialogueController != null) { dialogueController.ExitDialogue(); } } /// /// Set the active dialogue controller for the dialogue display to communicate with. /// /// The dialogue controller to use. public void SetActiveDialogueController(DialogueController dialogueController) { dialogueController.AddDialogueListener(this); this.dialogueController = dialogueController; } /// /// This method is called when options are displayed and sets the IsCurrentlyInConversation flag to false. /// /// The List of dialogue options being displayed. public override void OnDisplayOptions(List options) { base.OnDisplayOptions(options); foreach(DialogueListener listener in dialogueListeners) { listener.OnDisplayOptions(options); } IsCurrentlyInConversation = false; } /// /// This method is called when a line of dialogue is being displayed and sets the IsCurrentlyInConversation flag to true. /// /// The line of dialogue being displayed. public override void OnDisplayLine(ConversationLine line) { base.OnDisplayLine(line); foreach(DialogueListener listener in dialogueListeners) { listener.OnDisplayLine(line); } IsCurrentlyInConversation = true; } /// /// This method is called when the dialogue exits playback and sets the IsCurrentlyInConversation flag to false; /// /// The name of the exit point where the dialogue exited. public override void OnDialogueExited(string exitName) { base.OnDialogueExited(exitName); IsPlaying = false; dialogueController.RemoveDialogueListener(this); foreach(DialogueListener listener in dialogueListeners) { listener.OnDialogueExited(exitName); } IsCurrentlyInConversation = false; } /// public override void OnExitCompleted() { base.OnExitCompleted(); foreach (DialogueListener listener in dialogueListeners) { listener.OnExitCompleted(); } } /// /// Returns the conversation display being used by this dialogue display. /// /// The conversation display being used. public abstract ConversationDisplay GetConversationDisplay(); /// /// Sets the conversation display used by the dialogue display. /// /// The conversation display to use. public abstract void SetConversationDisplay(ConversationDisplay convoDisplay); /// /// Returns the option display being used by this dialogue display. /// /// The option display being used. public abstract OptionDisplay GetOptionDisplay(); /// /// Sets the option display being used by this dialogue display. /// /// The option display to use. public abstract void SetOptionDisplay(OptionDisplay optionDisplay); /// /// Returns the continue display being used by this dialogue display. /// /// The continue display being used. public abstract ContinueDisplay GetContinueDisplay(); /// /// Sets the continue display used by this dialogue display. /// /// The continue display to use. public abstract void SetContinueDisplay(ContinueDisplay continueDisplaY); /// /// Gets the TextInputDisplay used to retrieve text input from the player. /// /// public abstract TextInputDisplay GetTextInputDisplay(); /// /// Sets the TextInputDisplay used to retrieve text input from the player. /// /// The TextInputDisplay to use. public abstract void SetTextInputDisplay(TextInputDisplay textInputDisplay); /// /// Gets the dialogue display instance. /// public static AbstractDialogueDisplay Instance { get { return instance; } } /// /// Returns the current DialogueController being used by this dialogue display, if there is one. /// public DialogueController CurrentController { get { return dialogueController; } } /// /// Gets or sets whether the dialogue is currently allowing options to be selected by the player. /// public bool IsOptionSelectionAllowed { get { return isOptionSelectionAllowed; } set { isOptionSelectionAllowed = value; } } /// /// Gets or sets whether the player is allowed to immediately exit the dialogue by pressing a button. /// public bool IsQuickExitAllowed { get { return this.allowQuickExit; } set { this.allowQuickExit = value; } } /// /// Gets or sets whether continuation is allowed by the player by pressing a button. /// public bool IsContinueAllowed { get { return this.isContinueAllowed; } set { this.isContinueAllowed = value; } } /// /// Allows the current conversation to be continued. /// public virtual void AllowContinue() { this.isContinueAllowed = true; if (onContinueEnabled != null) { onContinueEnabled.Invoke(); } } /// /// Disallows continuation of the current conversation. /// public virtual void DisableContinue() { this.isContinueAllowed = false; if (onContinueDisabled != null) { onContinueDisabled.Invoke(); } } /// /// Allows option selection. /// public void AllowOptionSelection() { this.isOptionSelectionAllowed = true; if (onOptionSelectionEnabled != null) { onOptionSelectionEnabled.Invoke(); } } /// /// Disallows option selection. /// public void DisableOptionSelection() { this.isOptionSelectionAllowed = false; if (onOptionSelectionDisabled != null) { onOptionSelectionDisabled.Invoke(); } } /// /// This method is called when a continue occurs. /// public override void OnContinue() { base.OnContinue(); foreach(DialogueListener listener in dialogueListeners) { listener.OnContinue(); } DisableContinue(); } /// /// Sets the language used by the dialogue display. /// /// The ISO-639 language code to use. public void SetLanguage(string languageCode) { EasyTalkGameState.Instance.Language = languageCode; } /// /// This method is called whenever the language is changed by setting the language on the EasyTalkGameState. /// /// The previous ISO-639 language code being used. /// The new ISO-639 language code to use. protected void LanguageChanged(string oldLanguage, string newLanguage) { UpdateFontsForLanguage(this); TranslateComponents(); } /// /// Translates the text set on the conversation display and option display based on the currently set language of EasyTalkGameState.Instance. /// protected void TranslateComponents() { ConversationDisplay convoDisplay = GetConversationDisplay(); convoDisplay.TranslateText(this.dialogueController); OptionDisplay optionDisplay = GetOptionDisplay(); optionDisplay.TranslateOptions(this.dialogueController); } /// /// Updates all of the fonts on child text components based on the language currently being used. /// /// The component to update. protected void UpdateFontsForLanguage(Component component) { LanguageFontOverride fontOverride = dialogueSettings.LanguageFontOverrides.GetOverrideForLanguage(EasyTalkGameState.Instance.Language); //Update fonts for normal standard Unity text components. ComponentFontManager.UpdateFonts(component, fontOverride); #if TEXTMESHPRO_INSTALLED //Update fonts for TextMeshPro text components ComponentFontManager.UpdateTextMeshProFonts(component, fontOverride); #endif } /// /// Adds the specified Dialogue Listener to the 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 list of listeners. /// /// The Dialogue Listener to remove. public void RemoveDialogueListener(DialogueListener dialogueListener) { if (dialogueListeners.Contains(dialogueListener)) { dialogueListeners.Remove(dialogueListener); } } /// /// Removes all Dialogue Listeners of the display. /// public void RemoveDialogueListeners() { dialogueListeners.Clear(); } /// public override void OnOptionChosen(DialogueOption option) { base.OnOptionChosen(option); foreach(DialogueListener listener in dialogueListeners) { listener.OnOptionChosen(option); } } /// public override void OnDialogueEntered(string entryPointName) { base.OnDialogueEntered(entryPointName); IsPlaying = true; foreach (DialogueListener listener in dialogueListeners) { listener.OnDialogueEntered(entryPointName); } } /// public override void OnStory(string storyText) { base.OnStory(storyText); foreach (DialogueListener listener in dialogueListeners) { listener.OnStory(storyText); } } /// public override void OnVariableUpdated(string variableName, object value) { base.OnVariableUpdated(variableName, value); foreach (DialogueListener listener in dialogueListeners) { listener.OnVariableUpdated(variableName, value); } } /// public override void OnCharacterChanged(string oldCharacterName, string newCharacterName) { base.OnCharacterChanged(oldCharacterName, newCharacterName); foreach (DialogueListener listener in dialogueListeners) { listener.OnCharacterChanged(oldCharacterName, newCharacterName); } } /// public override void OnAudioStarted(ConversationLine line) { base.OnAudioStarted(line); foreach (DialogueListener listener in dialogueListeners) { listener.OnAudioStarted(line); } } /// public override void OnAudioCompleted(ConversationLine line, bool forceStopped) { base.OnAudioCompleted(line, forceStopped); foreach (DialogueListener listener in dialogueListeners) { listener.OnAudioCompleted(line, forceStopped); } } /// public override void OnActivateKey(string key) { base.OnActivateKey(key); foreach (DialogueListener listener in dialogueListeners) { listener.OnActivateKey(key); } } /// public override void Wait(float timeInSeconds) { base.Wait(timeInSeconds); foreach (DialogueListener listener in dialogueListeners) { listener.Wait(timeInSeconds); } } /// public override void OnConversationEnding(ConversationLine line, Node nextNode) { base.OnConversationEnding(line, nextNode); foreach (DialogueListener listener in dialogueListeners) { listener.OnConversationEnding(line, nextNode); } } /// public override void OnNodeChanged(Node node) { base.OnNodeChanged(node); foreach (DialogueListener listener in dialogueListeners) { listener.OnNodeChanged(node); } } /// public override void OnPause(string signal) { base.OnPause(signal); foreach (DialogueListener listener in dialogueListeners) { listener.OnPause(signal); } } /// 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 OnWaitingForNodeEvaluation(Node asyncNode) { base.OnWaitingForNodeEvaluation(asyncNode); foreach(DialogueListener listener in dialogueListeners) { listener.OnWaitingForNodeEvaluation(asyncNode); } } /// 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(); } }*/ /// /// Gets or sets the EasyTalk Dialogue Settings used by the display. /// public EasyTalkDialogueSettings DialogueSettings { get { return this.dialogueSettings; } set { this.dialogueSettings = value; } } /// /// Internal class used to keep track of font and font size settings for standard text components used by a dialogue display. /// public class TextFontSettings { public Font font; public int minFontSize; public int maxFontSize; public TextFontSettings(Font font, int minFontSize, int maxFontSize) { this.font = font; this.minFontSize = minFontSize; this.maxFontSize = maxFontSize; } } #if TEXTMESHPRO_INSTALLED /// /// Internal class used to keep track of font and font size settings for TextMeshPro text components used by a dialogue display. /// public class TMPTextFontSettings { public TMP_FontAsset font; public float minFontSize; public float maxFontSize; public TMPTextFontSettings(TMP_FontAsset font, float minFontSize, float maxFontSize) { this.font = font; this.minFontSize = minFontSize; this.maxFontSize = maxFontSize; } } #endif } }