// Copyright (c) Pixel Crushers. All rights reserved. using System; using UnityEngine; namespace PixelCrushers.DialogueSystem { /// /// This abstract class forms the base for many IDialogueUI implementations. It implements /// common code so that the specific implementation for each GUI system need only deal /// with the data structures specific to each GUI system. /// public abstract class AbstractDialogueUI : MonoBehaviour, IDialogueUI { /// /// Gets the user interface root. /// /// /// The user interface root. /// public abstract AbstractUIRoot uiRootControls { get; } /// /// Gets the dialogue controls. /// /// /// The dialogue controls. /// public abstract AbstractDialogueUIControls dialogueControls { get; } /// /// Gets the QTE (Quick Time Event) indicators. /// /// /// The QTE indicators. /// public abstract AbstractUIQTEControls qteControls { get; } /// /// Gets the alert message controls. /// /// /// The alert message controls /// public abstract AbstractUIAlertControls alertControls { get; } /// /// Occurs when the player selects a response. /// public event EventHandler SelectedResponseHandler; /// /// Gets or sets a value indicating whether the dialogue UI (conversation interface) is open. /// /// /// true if open; otherwise, false. /// public bool isOpen { get; set; } private bool m_hasOpenedBefore = false; /// @cond FOR_V1_COMPATIBILITY public bool IsOpen { get { return isOpen; } set { isOpen = value; } } /// @endcond /// /// Sets up the component. /// public virtual void Awake() { isOpen = false; SelectedResponseHandler = null; } /// /// Starts this instance by hiding everything. The only exception is if an alert message is /// already showing; it keeps this message visible. /// public virtual void Start() { if ((uiRootControls == null) || (dialogueControls == null) || (qteControls == null) || (alertControls == null)) { enabled = false; } else { uiRootControls.Show(); if (!isOpen) dialogueControls.Hide(); qteControls.Hide(); if (!alertControls.isVisible) alertControls.Hide(); if (isOpen) Open(); if (!(alertControls.isVisible || isOpen)) uiRootControls.Hide(); } } /// /// Opens the conversation GUI. /// public virtual void Open() { m_hasOpenedBefore = true; dialogueControls.ShowPanel(); uiRootControls.Show(); isOpen = true; } /// /// Closes the conversation GUI. /// public virtual void Close() { dialogueControls.Hide(); if (!AreNonDialogueControlsVisible) uiRootControls.Hide(); isOpen = false; } /// /// Gets a value indicating whether non-conversation controls (e.g., alert message or QTEs) /// are visible. /// /// /// true if non-conversation controls are visible; otherwise, false. /// protected virtual bool AreNonDialogueControlsVisible { get { return alertControls.isVisible || qteControls.areVisible; } } /// /// Shows an alert. /// /// /// Message to show. /// /// /// Duration in seconds. /// public virtual void ShowAlert(string message, float duration) { if (!isOpen) { if (!m_hasOpenedBefore) dialogueControls.Hide(); uiRootControls.Show(); } alertControls.ShowMessage(message, duration); } /// /// Hides the alert if it's showing. /// public virtual void HideAlert() { if (alertControls.isVisible) { alertControls.Hide(); if (!(isOpen || qteControls.areVisible)) uiRootControls.Hide(); } } /// /// Hides the alert if it's showing. /// Virtual so StandardDialogueUI can override to clear alert queue, too. /// public virtual void HideAllAlerts() { HideAlert(); } /// /// Updates this instance by hiding the alert message when it's done. /// public virtual void Update() { if (alertControls.isVisible && alertControls.IsDone) alertControls.Hide(); } /// /// Shows the subtitle (NPC or PC) based on the character type. /// /// /// Subtitle to show. /// public virtual void ShowSubtitle(Subtitle subtitle) { SetSubtitle(subtitle, true); } /// /// Hides the subtitle based on its character type (PC or NPC). /// /// /// Subtitle to hide. /// public virtual void HideSubtitle(Subtitle subtitle) { SetSubtitle(subtitle, false); } /// /// Shows the continue button. /// /// Subtitle. public virtual void ShowContinueButton(Subtitle subtitle) { var subtitleControls = GetSubtitleControls(subtitle); if (subtitleControls != null) { subtitleControls.ShowContinueButton(); } } /// /// Hides the continue button. Call this after showing the subtitle. /// The ConversationView uses this to hide the continue button if the /// display setting for continue buttons is set to NotBeforeResponseMenu. /// /// Subtitle. public virtual void HideContinueButton(Subtitle subtitle) { var subtitleControls = GetSubtitleControls(subtitle); if (subtitleControls != null) { subtitleControls.HideContinueButton(); } } /// /// Sets a subtitle's content and visibility. /// /// /// Subtitle. The speaker recorded in the subtitle determines whether the NPC or /// PC subtitle controls are used. /// /// /// true to show; false to hide. /// protected virtual void SetSubtitle(Subtitle subtitle, bool value) { var subtitleControls = GetSubtitleControls(subtitle); if (subtitleControls != null) { if (value == true) { subtitleControls.ShowSubtitle(subtitle); } else { subtitleControls.Hide(); } } } /// /// Gets the appropriate subtitle control (PC or NPC) for a subtitle. /// /// /// The subtitle controls to use for a subtitle. /// /// /// Subtitle to use; the subtitle's speakerInfo property indicates whether the subtitle is /// a PC or NPC line. /// private AbstractUISubtitleControls GetSubtitleControls(Subtitle subtitle) { return (subtitle == null) ? null : (subtitle.speakerInfo.characterType == CharacterType.NPC) ? dialogueControls.npcSubtitleControls : dialogueControls.pcSubtitleControls; } /// /// Shows the player responses menu. /// /// /// The last subtitle, shown as a reminder. /// /// /// Responses. /// /// /// If not 0, the duration in seconds that the player has to choose a response; /// otherwise the currently-focused response is auto-selected. If no response is /// focused (e.g., hovered over), the first response is auto-selected. If 0, /// there is no timeout; the player can take as long as desired to choose a response. /// public virtual void ShowResponses(Subtitle subtitle, Response[] responses, float timeout) { try { if (dialogueControls == null) { Debug.LogError(DialogueDebug.Prefix + ": In ShowResponses(): The dialogue UI's main dialogue controls field is not set.", this); } else if (dialogueControls.responseMenuControls == null) { Debug.LogError(DialogueDebug.Prefix + ": In ShowResponses(): The dialogue UI's response menu controls field is not set.", this); } else if (this.transform == null) { Debug.LogError(DialogueDebug.Prefix + ": In ShowResponses(): The dialogue UI's transform is null.", this); } else { dialogueControls.responseMenuControls.ShowResponses(subtitle, responses, this.transform); if (timeout > 0) dialogueControls.responseMenuControls.StartTimer(timeout); } } catch (NullReferenceException e) { Debug.LogError(DialogueDebug.Prefix + ": In ShowResponses(): " + e.Message); } } /// /// Hides the player response menu. /// public virtual void HideResponses() { dialogueControls.responseMenuControls.Hide(); } /// /// Shows a QTE indicator. /// /// /// Index of the QTE indicator. /// public virtual void ShowQTEIndicator(int index) { qteControls.ShowIndicator(index); } /// /// Hides a QTE indicator. /// /// /// Index of the QTE indicator. /// public virtual void HideQTEIndicator(int index) { qteControls.HideIndicator(index); } /// /// Handles response button clicks. /// /// /// The Response assigned to the button. This is passed back to the handler. /// public virtual void OnClick(object data) { if (SelectedResponseHandler != null) SelectedResponseHandler(this, new SelectedResponseEventArgs(data as Response)); } /// /// Handles the continue button being clicked. This sends OnConversationContinue to the /// DialogueManager object so the ConversationView knows to progress the conversation. /// public virtual void OnContinue() { OnContinueAlert(); OnContinueConversation(); } public virtual void OnContinueAlert() { if (alertControls.isVisible) HideAlert(); } public virtual void OnContinueConversation() { if (isOpen) DialogueManager.instance.SendMessage(DialogueSystemMessages.OnConversationContinue, (IDialogueUI)this, SendMessageOptions.DontRequireReceiver); } /// /// Sets the PC portrait name and sprite. /// /// Portrait sprite. /// Portrait name. public virtual void SetPCPortrait(Sprite portraitSprite, string portraitName) { dialogueControls.responseMenuControls.SetPCPortrait(portraitSprite, portraitName); } [System.Obsolete("Use SetPCPortrait(Sprite,string) instead.")] public virtual void SetPCPortrait(Texture2D portraitTexture, string portraitName) { dialogueControls.responseMenuControls.SetPCPortrait(UITools.CreateSprite(portraitTexture), portraitName); } /// /// Sets the portrait sprite for an actor. /// This is used to immediately update the GUI control if the SetPortrait() sequencer /// command changes the portrait sprite. /// /// Actor name in database. /// Portrait sprite. public virtual void SetActorPortraitSprite(string actorName, Sprite portraitSprite) { dialogueControls.npcSubtitleControls.SetActorPortraitSprite(actorName, portraitSprite); dialogueControls.pcSubtitleControls.SetActorPortraitSprite(actorName, portraitSprite); dialogueControls.responseMenuControls.SetActorPortraitSprite(actorName, portraitSprite); } /// /// Gets a valid portrait sprite. If the provided portraitSprite is null, grabs the default /// sprites from the database. /// /// The valid portrait sprite. /// Actor name. /// Portrait sprite. public static Sprite GetValidPortraitSprite(string actorName, Sprite portraitSprite) { if (portraitSprite != null) { return portraitSprite; } else { var actor = DialogueManager.masterDatabase.GetActor(actorName); return (actor != null) ? actor.GetPortraitSprite() : null; } } [System.Obsolete("Use GetValidPortraitSprite instead.")] public static Texture2D GetValidPortraitTexture(string actorName, Texture2D portraitTexture) { if (portraitTexture != null) { return portraitTexture; } else { var actor = DialogueManager.masterDatabase.GetActor(actorName); return (actor != null) ? actor.portrait : null; } } } }