// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; namespace PixelCrushers.DialogueSystem { /// /// Display settings to apply to the dialogue UI and sequencer. /// [System.Serializable] public class DisplaySettings { public DisplaySettings() { } public DisplaySettings(DisplaySettings source) { this.conversationOverrideSettings = source.conversationOverrideSettings; this.dialogueUI = source.dialogueUI; this.defaultCanvas = source.defaultCanvas; this.localizationSettings = new LocalizationSettings(source.localizationSettings); this.subtitleSettings = new SubtitleSettings(source.subtitleSettings); this.cameraSettings = new CameraSettings(source.cameraSettings); this.inputSettings = new InputSettings(source.inputSettings); this.barkSettings = new BarkSettings(source.barkSettings); this.alertSettings = new AlertSettings(source.alertSettings); } [HideInInspector] public ConversationOverrideDisplaySettings conversationOverrideSettings = null; [Tooltip("Assign a GameObject that contains an active dialogue UI component. Can be a prefab. If unassigned, Dialogue Manager will search its children for an active dialogue UI component.")] public GameObject dialogueUI; [Tooltip("Optional. Assign Canvas into which dialogue UI will be instantiated if it's a prefab.")] public Canvas defaultCanvas; [System.Serializable] public class LocalizationSettings { public LocalizationSettings() { } public LocalizationSettings(LocalizationSettings source) { this.language = source.language; this.useSystemLanguage = source.useSystemLanguage; this.textTable = source.textTable; } /// /// The current language, or blank to use the default language. /// [Tooltip("Current language, or blank to use the default language.")] public string language = string.Empty; /// /// Set true to automatically use the system language at startup. /// [Tooltip("Use the system language at startup.")] public bool useSystemLanguage = false; /// /// An optional text table. Used by DialogueSystemController.GetLocalizedText() /// and ShowAlert() if assigned. /// [Tooltip("Optional localized text for alerts and other general text. Note: Now uses Text Table instead of Localized Text Table.")] public TextTable textTable = null; //---Was: public LocalizedTextTable localizedText = null; } public LocalizationSettings localizationSettings = new LocalizationSettings(); [System.Serializable] public class SubtitleSettings { public SubtitleSettings() { } public SubtitleSettings(SubtitleSettings source) { this.showNPCSubtitlesDuringLine = source.showNPCSubtitlesDuringLine; this.showNPCSubtitlesWithResponses = source.showNPCSubtitlesWithResponses; this.showPCSubtitlesDuringLine = source.showPCSubtitlesDuringLine; this.allowPCSubtitleReminders = source.allowPCSubtitleReminders; this.skipPCSubtitleAfterResponseMenu = source.skipPCSubtitleAfterResponseMenu; this.subtitleCharsPerSecond = source.subtitleCharsPerSecond; this.minSubtitleSeconds = source.minSubtitleSeconds; this.continueButton = source.continueButton; this.requireContinueOnLastLine = source.requireContinueOnLastLine; this.richTextEmphases = source.richTextEmphases; this.informSequenceStartAndEnd = source.informSequenceStartAndEnd; } /// /// Specifies whether to show NPC subtitles while speaking a line of dialogue. /// [Tooltip("Show NPC subtitle text while NPC speaks a line of dialogue.")] public bool showNPCSubtitlesDuringLine = true; /// /// Specifies whether to should show NPC subtitles while presenting the player's follow-up /// responses. /// [Tooltip("Show NPC subtitle reminder text while showing the player response menu. If you're using Standard Dialogue UI, the subtitle panel's Visiblity value takes precedenece over this.")] public bool showNPCSubtitlesWithResponses = true; /// /// Specifies whether to show PC subtitles while speaking a line of dialogue. /// [Tooltip("Show PC subtitle text while PC speaks a line of dialogue. If Skip PC Subtitle After Response Menu (below) is ticked, PC subtitles from response menu selections will be skipped.")] public bool showPCSubtitlesDuringLine = false; /// /// Set true to allow PC subtitles to be used for the reminder line /// during the response menu. /// [Tooltip("Allow PC subtitles to be used for reminder text while showing the response menu.")] public bool allowPCSubtitleReminders = false; /// /// If the PC's subtitle came from a response menu selection, don't show the subtitle even if showPCSubtitlesDuringLine is true. /// [Tooltip("If the PC's subtitle came from a response menu selection, don't show the subtitle even if Show PC Subtitles During Line is ticked.")] public bool skipPCSubtitleAfterResponseMenu = false; /// /// The default subtitle characters per second. This value is used to compute the default /// duration to display a subtitle if no sequence is specified for a line of dialogue. /// This value is also used when displaying alerts. /// [Tooltip("Used to compute default duration to display subtitle. Typewriter effects have their own separate setting.")] public float subtitleCharsPerSecond = 30f; /// /// The minimum duration to display a subtitle if no sequence is specified for a line of /// dialogue. This value is also used when displaying alerts. /// [Tooltip("Minimum default duration to display subtitle.")] public float minSubtitleSeconds = 2f; public enum ContinueButtonMode { /// /// Never wait for the continue button. Use this if your UI doesn't have continue buttons. /// Never, /// /// Always wait for the continue button. /// Always, /// /// Show the continue button but don't wait for it. /// Optional, /// /// Wait for the continue button, except when the response menu is next show but don't wait. /// OptionalBeforeResponseMenu, /// /// Wait for the continue button, except when the response menu is next hide it. /// NotBeforeResponseMenu, /// /// Wait for the continue button, except when a PC auto-select response or response /// menu is next, show but don't wait. /// OptionalBeforePCAutoresponseOrMenu, /// /// Wait for the continue button, except with a PC auto-select response or response /// menu is next, hide it. /// NotBeforePCAutoresponseOrMenu, /// /// Wait for the continue button, except when delivering PC lines show but don't wait. /// OptionalForPC, /// /// Wait for the continue button except when delivering PC lines. /// NotForPC, /// /// Wait for the continue button, except when preceding response menus or delivering PC lines don't wait. /// OptionalForPCOrBeforeResponseMenu, /// /// Wait for the continue button only for NPC lines that don't precede response menus. /// NotForPCOrBeforeResponseMenu, /// /// Wait for the continue button, except for PC lines and lines preceding a response menu or PC auto-select response don't wait. /// OptionalForPCOrBeforePCAutoresponseOrMenu, /// /// Wait for the continue button only for NPC lines that don't precede response menus or PC auto-select responses. /// NotForPCOrBeforePCAutoresponseOrMenu, /// /// Wait for continue button for PC lines but not for NPC lines. /// OnlyForPC } /// /// How to handle continue buttons. /// [Tooltip("How to handle continue buttons.")] public ContinueButtonMode continueButton = ContinueButtonMode.Never; [Tooltip("If ticked, always require continue button on subtitle that ends conversation. Overrides Continue Button dropdown above.")] public bool requireContinueOnLastLine = false; /// /// Set true to convert "[em#]" tags to rich text codes in formatted text. /// Your implementation of IDialogueUI must support rich text. /// [Tooltip("Use rich text codes for [em#] markup tags. If unticked, [em#] tag will apply color to entire text.")] public bool richTextEmphases = true; /// /// Set true to send OnSequenceStart and OnSequenceEnd messages with /// every dialogue entry's sequence. /// [Tooltip("Send OnSequenceStart and OnSequenceEnd messages with every dialogue entry's sequence.")] public bool informSequenceStartAndEnd = false; } /// /// The subtitle settings. /// public SubtitleSettings subtitleSettings = new SubtitleSettings(); [System.Serializable] public class CameraSettings { public CameraSettings() { } public CameraSettings(CameraSettings source) { this.sequencerCamera = source.sequencerCamera; this.alternateCameraObject = source.alternateCameraObject; this.cameraAngles = source.cameraAngles; this.keepCameraPositionAtConversationEnd = source.keepCameraPositionAtConversationEnd; this.cameraEasing = Tweener.Easing.Linear; this.showSubtitleOnEmptyContinue = source.showSubtitleOnEmptyContinue; this.defaultSequence = source.defaultSequence; this.defaultPlayerSequence = source.defaultPlayerSequence; this.defaultResponseMenuSequence = source.defaultResponseMenuSequence; this.entrytagFormat = source.entrytagFormat; this.reportMissingAudioFiles = source.reportMissingAudioFiles; this.disableInternalSequencerCommands = source.disableInternalSequencerCommands; } /// /// The camera (or prefab) to use for sequences. If unassigned, the sequencer will use the /// main camera; when the sequence is done, it will restore the main camera's original /// position. /// [Tooltip("Camera or prefab to use for sequences. If unassigned, sequences use the current main camera.")] public Camera sequencerCamera = null; /// /// An alternate camera object to use instead of sequencerCamera. Use this, for example, /// if you have an Oculus VR GameObject that's a parent of two cameras. Currently this /// must be an object in the scene, not a prefab. /// [Tooltip("If assigned, use instead of Sequencer Camera -- for example, Oculus VR GameObject. Can't be a prefab.")] public GameObject alternateCameraObject = null; /// /// The camera angle object (or prefab) to use for the "Camera()" sequence command. See /// @ref sequencerCommandCamera for more information. /// [Tooltip("Camera angle object or prefab. If unassigned, use default camera angle definitions.")] public GameObject cameraAngles = null; /// /// Specifies how Camera() commands should move the camera. /// [Tooltip("Specifies how Camera() commands should move the camera.")] public Tweener.Easing cameraEasing = Tweener.Easing.Linear; /// /// If conversation's sequences use Main Camera, leave camera in current position at end of conversation instead of restoring pre-conversation position. /// [Tooltip("If conversation's sequences use Main Camera, leave camera in current position at end of conversation instead of restoring pre-conversation position.")] public bool keepCameraPositionAtConversationEnd = false; /// /// Show subtitle if sequence is only 'Continue()'. Typically only useful in UIs that accumulate text. /// [Tooltip("Show subtitle if sequence is only 'Continue()'. Typically only useful in UIs that accumulate text.")] public bool showSubtitleOnEmptyContinue = false; /// /// The default sequence to use if the dialogue entry doesn't have a sequence defined /// in its Sequence field. See @ref dialogueCreation and @ref sequencer for /// more information. The special keyword "{{end}}" gets replaced by the default /// duration for the subtitle being displayed. /// [Tooltip("Used when a dialogue entry doesn't define its own Sequence. Set to Delay({{end}}) to leave the camera untouched.")] [TextArea] public string defaultSequence = "Delay({{end}})"; /// /// If defined, overrides Default Sequence for player (PC) lines only. /// [Tooltip("If defined, overrides Default Sequence for player (PC) lines only.")] [TextArea] public string defaultPlayerSequence = string.Empty; /// /// Used when a dialogue entry doesn't define its own Response Menu Sequence. /// [Tooltip("Used when a dialogue entry doesn't define its own Response Menu Sequence.")] [TextArea] public string defaultResponseMenuSequence = string.Empty; /// /// The format to use for the entrytag keyword. /// [Tooltip("Format to use for the 'entrytag' keyword.")] public EntrytagFormat entrytagFormat = EntrytagFormat.ActorName_ConversationID_EntryID; /// /// By default, Audio() and AudioWait() sequencer commands don't report /// missing audio files to reduce Console spam during development. Set this /// true to report missing audio files. /// [Tooltip("By default, Audio() and AudioWait() sequencer commands don't report missing audio files to reduce Console spam during development.")] public bool reportMissingAudioFiles = false; /// /// Set true to disable the internal sequencer commands -- for example, if you /// want to replace them with your own. /// [HideInInspector] public bool disableInternalSequencerCommands = false; } /// /// The camera settings. /// public CameraSettings cameraSettings = new CameraSettings(); [System.Serializable] public class InputSettings { public InputSettings() { } public InputSettings(InputSettings source) { this.alwaysForceResponseMenu = source.alwaysForceResponseMenu; this.includeInvalidEntries = source.includeInvalidEntries; this.responseTimeout = source.responseTimeout; this.responseTimeoutAction = source.responseTimeoutAction; this.emTagForOldResponses = source.emTagForOldResponses; this.emTagForInvalidResponses = source.emTagForInvalidResponses; this.qteButtons = source.qteButtons; this.cancel = source.cancel; this.cancelConversation = source.cancelConversation; } /// /// If true, always forces the response menu even if there's only one response. /// If false, you can use the [f] tag to force a response. /// [Tooltip("Show the response menu even if there's only one response.")] public bool alwaysForceResponseMenu = true; /// /// If `true`, includes responses whose Conditions are false. The `enabled` field of /// those responses will be `false`. /// [Tooltip("Include responses whose Conditions are false. typically shown in a disabled state.")] public bool includeInvalidEntries = false; /// /// 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. /// [Tooltip("If nonzero, the duration in seconds until the response menu times out.")] public float responseTimeout = 0f; /// /// The response timeout action. /// [Tooltip("What to do if the response menu times out.")] public ResponseTimeoutAction responseTimeoutAction = ResponseTimeoutAction.ChooseFirstResponse; /// /// The em tag to wrap around old responses. A response is old if its SimStatus /// is "WasDisplayed". You can change this from EmTag.None if you want to visually /// mark old responses in the player response menu. /// [Tooltip("The [em#] tag to wrap around responses that have been previously chosen.")] public EmTag emTagForOldResponses = EmTag.None; /// /// The em tag to wrap around invalid responses. You can change this from EmTag.None /// if you want to visually mark invalid responses in the player response menu. /// [Tooltip("The [em#] tag to wrap around invalid responses. These responses are only shown if Include Invalid Entries is ticked.")] public EmTag emTagForInvalidResponses = EmTag.None; /// /// The buttons QTE (Quick Time Event) buttons. QTE 0 & 1 default to the buttons /// Fire1 and Fire2. /// [Tooltip("Input buttons mapped to QTEs.")] public string[] qteButtons = new string[] { "Fire1", "Fire2" }; /// /// The key and/or button that allows the player to cancel subtitle sequences. /// [Tooltip("Key or button that cancels subtitle sequences.")] public InputTrigger cancel = new InputTrigger(KeyCode.Escape); /// /// The key and/or button that allows the player to cancel conversations. /// [Tooltip("Key or button that cancels active conversation while in response menu.")] public InputTrigger cancelConversation = new InputTrigger(KeyCode.Escape); } /// /// The input settings. /// public InputSettings inputSettings = new InputSettings(); [System.Serializable] public class BarkSettings { public BarkSettings() { } public BarkSettings(BarkSettings source) { this.allowBarksDuringConversations = source.allowBarksDuringConversations; this.barkCharsPerSecond = source.barkCharsPerSecond; this.minBarkSeconds = source.minBarkSeconds; this.defaultBarkSequence = source.defaultBarkSequence; } /// /// Set true to allow barks to play during conversations. /// [Tooltip("Allow barks to play during conversations.")] public bool allowBarksDuringConversations = true; /// /// Show barks for this many characters per second. If zero, use Subtitle Settings > Subtitle Chars Per Second. /// [Tooltip("Show barks for this many characters per second. If zero, use Subtitle Settings > Subtitle Chars Per Second.")] public float barkCharsPerSecond = 0; /// /// Show barks for at least this many seconds. If zero, use Subtitle Settings > Min Subtitle Seconds. /// [Tooltip("Show barks for at least this many seconds. If zero, use Subtitle Settings > Min Subtitle Seconds.")] public float minBarkSeconds = 0; /// /// If non-blank, play this sequence with barks that don't specify their own Sequence. /// [Tooltip("If non-blank, play this sequence with barks that don't specify their own Sequence.")] public string defaultBarkSequence = string.Empty; } /// /// The gameplay alert message settings. /// public BarkSettings barkSettings = new BarkSettings(); [System.Serializable] public class AlertSettings { public AlertSettings() { } public AlertSettings(AlertSettings source) { this.allowAlertsDuringConversations = source.allowAlertsDuringConversations; this.alertCheckFrequency = source.alertCheckFrequency; this.alertCharsPerSecond = source.alertCharsPerSecond; this.minAlertSeconds = source.minAlertSeconds; } /// /// Set true to allow the dialogue UI to show alerts during conversations. /// [Tooltip("Allow the dialogue UI to show alerts during conversations.")] public bool allowAlertsDuringConversations = false; /// /// How often to check if the Lua Variable['Alert'] has been set. To disable /// automatic monitoring, set this to 0. /// [Tooltip("If nonzero, check Variable['Alert'] at this frequency to show alert messages.")] public float alertCheckFrequency = 0f; /// /// Show alerts for this many characters per second. If zero, use Subtitle Settings > Subtitle Chars Per Second. /// [Tooltip("Show alerts for this many characters per second. If zero, use Subtitle Settings > Subtitle Chars Per Second.")] public float alertCharsPerSecond = 0; /// /// Show alerts for at least this many seconds. If zero, use Subtitle Settings > Min Subtitle Seconds. /// [Tooltip("Show alerts for at least this many seconds. If zero, use Subtitle Settings > Min Subtitle Seconds.")] public float minAlertSeconds = 0; } /// /// The gameplay alert message settings. /// public AlertSettings alertSettings = new AlertSettings(); public bool ShouldUseOverrides() { return (conversationOverrideSettings != null) && conversationOverrideSettings.useOverrides; } public bool ShouldUseSubtitleOverrides() { return ShouldUseOverrides() && conversationOverrideSettings.overrideSubtitleSettings; } public bool GetShowNPCSubtitlesDuringLine() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.showNPCSubtitlesDuringLine : ((subtitleSettings != null) ? subtitleSettings.showNPCSubtitlesDuringLine : true); } public bool GetShowNPCSubtitlesWithResponses() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.showNPCSubtitlesWithResponses : ((subtitleSettings != null) ? subtitleSettings.showNPCSubtitlesWithResponses : true); } public bool GetShowPCSubtitlesDuringLine() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.showPCSubtitlesDuringLine : ((subtitleSettings != null) ? subtitleSettings.showPCSubtitlesDuringLine : true); } public bool GetSkipPCSubtitleAfterResponseMenu() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.skipPCSubtitleAfterResponseMenu : ((subtitleSettings != null) ? subtitleSettings.skipPCSubtitleAfterResponseMenu : true); } public float GetSubtitleCharsPerSecond() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.subtitleCharsPerSecond : ((subtitleSettings != null) ? subtitleSettings.subtitleCharsPerSecond : 30); } public float GetMinSubtitleSeconds() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.minSubtitleSeconds : ((subtitleSettings != null) ? subtitleSettings.minSubtitleSeconds : 2); } public SubtitleSettings.ContinueButtonMode GetContinueButtonMode() { return ShouldUseSubtitleOverrides() ? conversationOverrideSettings.continueButton : ((subtitleSettings != null) ? subtitleSettings.continueButton : SubtitleSettings.ContinueButtonMode.Never); } public bool ShouldUseSequenceOverrides() { return ShouldUseOverrides() && conversationOverrideSettings.overrideSequenceSettings; } public string GetDefaultSequence() { return ShouldUseSequenceOverrides() && !string.IsNullOrEmpty(conversationOverrideSettings.defaultSequence) ? conversationOverrideSettings.defaultSequence : ((cameraSettings != null) ? cameraSettings.defaultSequence : string.Empty); } public string GetDefaultPlayerSequence() { return ShouldUseSequenceOverrides() && !string.IsNullOrEmpty(conversationOverrideSettings.defaultPlayerSequence) ? conversationOverrideSettings.defaultPlayerSequence : ((cameraSettings != null) ? cameraSettings.defaultPlayerSequence : string.Empty); } public string GetDefaultResponseMenuSequence() { return ShouldUseSequenceOverrides() && !string.IsNullOrEmpty(conversationOverrideSettings.defaultResponseMenuSequence) ? conversationOverrideSettings.defaultResponseMenuSequence : ((cameraSettings != null) ? cameraSettings.defaultResponseMenuSequence : string.Empty); } public bool ShouldUseInputOverrides() { return ShouldUseOverrides() && conversationOverrideSettings.overrideInputSettings; } public bool GetAlwaysForceResponseMenu() { return ShouldUseInputOverrides() ? conversationOverrideSettings.alwaysForceResponseMenu : ((inputSettings != null) ? inputSettings.alwaysForceResponseMenu : true); } public bool GetIncludeInvalidEntries() { return ShouldUseInputOverrides() ? conversationOverrideSettings.includeInvalidEntries : ((inputSettings != null) ? inputSettings.includeInvalidEntries : true); } public float GetResponseTimeout() { return ShouldUseInputOverrides() ? conversationOverrideSettings.responseTimeout : ((inputSettings != null) ? inputSettings.responseTimeout : 0); } public EmTag GetEmTagForOldResponses() { return ShouldUseInputOverrides() ? conversationOverrideSettings.emTagForOldResponses : ((inputSettings != null) ? inputSettings.emTagForOldResponses : EmTag.None); } public EmTag GetEmTagForInvalidResponses() { return ShouldUseInputOverrides() ? conversationOverrideSettings.emTagForInvalidResponses : ((inputSettings != null) ? inputSettings.emTagForInvalidResponses : EmTag.None); } public InputTrigger GetCancelSubtitleInput() { return ShouldUseInputOverrides() ? conversationOverrideSettings.cancelSubtitle : ((inputSettings != null) ? inputSettings.cancel : null); } public InputTrigger GetCancelConversationInput() { return ShouldUseInputOverrides() ? conversationOverrideSettings.cancelConversation : ((inputSettings != null) ? inputSettings.cancelConversation : null); } } /// /// Response timeout action specifies what to do if the response menu times out. /// public enum ResponseTimeoutAction { /// /// Auto-select the first menu choice. /// ChooseFirstResponse, /// /// Auto-select a random menu choice. /// ChooseRandomResponse, /// /// End of conversation. /// EndConversation, /// /// Auto-select current menu choice. /// ChooseCurrentResponse, /// /// Auto-select the last menu choice. /// ChooseLastResponse, /// /// Use a custom handler. /// Custom }; public enum EmTag { None, Em1, Em2, Em3, Em4 } }