#if UNITY_2021_1_OR_NEWER
// Copyright (c) Pixel Crushers. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace PixelCrushers.DialogueSystem.UIToolkit
{
///
/// Dialogue UI implementation for UI Toolkit.
///
public class UIToolkitDialogueUI : AbstractDialogueUI, IDialogueUI
{
[SerializeField] private UIToolkitRootElements rootElements;
[SerializeField] private UIToolkitAlertElements alertElements;
[SerializeField] private UIToolkitDialogueElements dialogueElements;
[SerializeField] private UIToolkitQTEElements qteElements;
public override AbstractUIRoot uiRootControls => rootElements;
public override AbstractDialogueUIControls dialogueControls => dialogueElements;
public override AbstractUIQTEControls qteControls => qteElements;
public override AbstractUIAlertControls alertControls => alertElements;
public override void Awake()
{
base.Awake();
dialogueElements.Initialize(OnContinueConversation, OnClick);
}
public override void Update()
{
base.Update();
(dialogueElements.responseMenuControls as UIToolkitResponseMenuElements).DoUpdate();
}
public override void Open()
{
base.Open();
OpenSubtitlePanelsOnStart();
}
public override void ShowSubtitle(Subtitle subtitle)
{
var panel = GetSubtitlePanel(subtitle);
if (panel != null)
{
HideOtherApplicablePanels(panel);
panel.ShowSubtitle(subtitle);
}
}
public override void HideSubtitle(Subtitle subtitle)
{
var panel = GetSubtitlePanel(subtitle);
if (panel != null && !panel.ShouldStayVisible) panel.Hide();
}
protected virtual UIToolkitSubtitleElements GetSubtitlePanel(int index)
{
return (0 <= index && index < dialogueElements.SubtitlePanelElements.Count)
? dialogueElements.SubtitlePanelElements[index] : null;
}
protected virtual UIToolkitSubtitleElements GetSubtitlePanel(Subtitle subtitle)
{
if (subtitle == null) return null;
// Check for override via [panel=#] tag:
var panel = GetSubtitlePanel(subtitle.formattedText.subtitlePanelNumber);
if (panel != null) return panel;
// Check for override on speaker's DialogueActor component:
var dialogueActor = DialogueActor.GetDialogueActorComponent(subtitle.speakerInfo.transform);
panel = GetDialogueActorSubtitlePanel(dialogueActor);
if (panel != null) return panel;
// Otherwise choose standard panel:
return subtitle.speakerInfo.isNPC
? dialogueElements.npcSubtitleControls as UIToolkitSubtitleElements
: dialogueElements.pcSubtitleControls as UIToolkitSubtitleElements;
}
protected virtual UIToolkitSubtitleElements GetDialogueActorSubtitlePanel(DialogueActor dialogueActor)
{
if (dialogueActor != null && dialogueActor.standardDialogueUISettings.subtitlePanelNumber != SubtitlePanelNumber.Default)
{
var index = PanelNumberUtility.GetSubtitlePanelIndex(dialogueActor.standardDialogueUISettings.subtitlePanelNumber);
return GetSubtitlePanel(index);
}
return null;
}
protected virtual void OpenSubtitlePanelsOnStart()
{
var conversation = DialogueManager.masterDatabase.GetConversation(DialogueManager.lastConversationStarted);
if (conversation == null) return;
HashSet checkedPanels = new HashSet();
HashSet checkedActorIDs = new HashSet();
// Check main Actor & Conversant:
var mainActorID = conversation.ActorID;
var mainActor = DialogueManager.masterDatabase.GetActor(DialogueActor.GetActorName(DialogueManager.currentActor));
if (mainActor != null) mainActorID = mainActor.id;
CheckActorIDOnStartConversation(mainActorID, checkedActorIDs, checkedPanels);
CheckActorIDOnStartConversation(conversation.ConversantID, checkedActorIDs, checkedPanels);
// Check other actors:
for (int i = 0; i < conversation.dialogueEntries.Count; i++)
{
var actorID = conversation.dialogueEntries[i].ActorID;
CheckActorIDOnStartConversation(actorID, checkedActorIDs, checkedPanels);
}
}
protected virtual void CheckActorIDOnStartConversation(int actorID, HashSet checkedActorIDs, HashSet checkedPanels)
{
if (checkedActorIDs.Contains(actorID)) return;
checkedActorIDs.Add(actorID);
var actor = DialogueManager.MasterDatabase.GetActor(actorID);
if (actor == null) return;
var actorTransform = GetActorTransform(actor.Name);
DialogueActor dialogueActor;
var defaultPanel = actor.IsPlayer ? dialogueElements.PCSubtitleElements : dialogueElements.NPCSubtitleElements;
var panel = GetActorTransformPanel(actorTransform, defaultPanel, out dialogueActor);
if (panel == null && actorTransform == null && Debug.isDebugBuild) Debug.LogWarning("Dialogue System: Can't determine what subtitle panel to use for " + actor.Name, actorTransform);
if (panel == null || checkedPanels.Contains(panel)) return;
checkedPanels.Add(panel);
if (panel.Visibility == UIVisibility.AlwaysFromStart)
{
var actorPortrait = (dialogueActor != null && dialogueActor.GetPortraitSprite() != null) ?
dialogueActor.GetPortraitSprite() : actor.GetPortraitSprite();
var actorName = CharacterInfo.GetLocalizedDisplayNameInDatabase(actor.Name);
panel.OpenOnStartConversation(actorPortrait, actorName, dialogueActor);
}
}
protected virtual Transform GetActorTransform(string actorName)
{
var actorTransform = CharacterInfo.GetRegisteredActorTransform(actorName);
if (actorTransform == null)
{
var go = GameObject.Find(actorName);
if (go != null) actorTransform = go.transform;
}
return actorTransform;
}
public virtual UIToolkitSubtitleElements GetActorTransformPanel(Transform speakerTransform, UIToolkitSubtitleElements defaultPanel,
out DialogueActor dialogueActor)
{
dialogueActor = null;
if (speakerTransform == null) return defaultPanel;
dialogueActor = DialogueActor.GetDialogueActorComponent(speakerTransform);
if (dialogueActor != null && dialogueActor.standardDialogueUISettings.subtitlePanelNumber != SubtitlePanelNumber.Default)
{
var panel = GetDialogueActorSubtitlePanel(dialogueActor);
if (panel != null) return panel;
}
return defaultPanel;
}
protected virtual void HideOtherApplicablePanels(UIToolkitSubtitleElements panel)
{
foreach (var otherPanel in dialogueElements.SubtitlePanelElements)
{
if (otherPanel.IsSamePanel(panel)) continue;
if (otherPanel.ShouldStayVisible) continue;
otherPanel.Hide();
}
}
public override void ShowResponses(Subtitle subtitle, Response[] responses, float timeout)
{
base.ShowResponses(subtitle, responses, timeout);
}
#region Static Utility Methods
public static void SetDisplay(VisualElement visualElement, bool value)
{
if (visualElement == null) return;
visualElement.style.display = value ? DisplayStyle.Flex : DisplayStyle.None;
}
public static bool IsVisible(VisualElement visualElement)
{
if (visualElement == null) return false;
return visualElement.style.display != DisplayStyle.None;
}
public static T GetVisualElement(UIDocument document, string visualElementName) where T : VisualElement
{
if (document == null || document.rootVisualElement == null) return null;
return document.rootVisualElement.Q(visualElementName);
}
public static void SetInteractable(VisualElement rootVisualElement, bool value)
{
if (rootVisualElement == null) return;
rootVisualElement.pickingMode = value ? PickingMode.Position : PickingMode.Ignore;
}
#endregion
}
}
#endif