Page 1 of 1

Show a different Dialogue UI if conversation has only one line

Posted: Fri Feb 25, 2022 7:13 am
by OddMan2
Hi again,
i am trying to achieve this behavior:
  • if a conversation has more than one line, use the standard ui
  • if a conversation has only one subtitle, (something like "i can't speak now.") use the bark ui or a different dialogue ui with speech bubbles in world space.
what is the best way to achieve this?
I saw the extra sequencer command to switch ui, but the demo is outdated and is broken
Thank you!

Re: Show a different Dialogue UI if conversation has only one line

Posted: Fri Feb 25, 2022 8:42 am
by Tony Li
Hi,

You'll first need to determine whether there's one line or multiple:

Code: Select all

bool HasMoreThanOneLine(string conversation, Transform actor, Transform conversant)
{
    var conversationModel = new ConversationModel(DialogueManager.masterDatabase, conversation, actor, conversant, false, null);
    var startNode = conversationModel.firstState;
    if (!startNode.hasAnyResponses)
    {
        return false; // The conversation has *no* lines.
    }
    else
    {
        var firstTextNode = conversationModel.GetState(startNode.firstNPCResponse.destinationEntry);
        if (firstTextNode.hasAnyResponses)
        {
            return true; // The conversation has more than one line.
        }
        else
        {
            return false; // The conversation has only one line.
        }
    }
}
[EDIT: Fixed a whole bunch of typos.]

Then you can bark or start a conversation based on that info:

Code: Select all

if (HasMoreThanOneLine(conversation, actor, conversant))
{
    DialogueManager.StartConversation(conversation, actor, conversant);
}
else
{
    DialogueManager.Bark(conversation, conversant);
}

Re: Show a different Dialogue UI if conversation has only one line

Posted: Fri Feb 25, 2022 12:18 pm
by OddMan2
Thank you!
I tried it and it works, but for some reason the HasMoreThanOneLine check always returns false, i don't understand why.
maybe you can help understand where the error is ?

i made a subclass of DialogueSystemTriggerInteractableTarget (i am using opsive UCC)

here's the code:

Code: Select all

using UnityEngine;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.Character;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Networking;
#endif

namespace PixelCrushers.DialogueSystem.OpsiveUCCSupport
{

    /// <summary>
    /// This is a Dialogue System Trigger that serves as a UCC Interactable target.
    /// </summary>
    public class CustomDialogueSystemTriggerInteractableTarget : DialogueSystemTriggerInteractableTarget, IInteractableTarget, IInteractableMessage
    {
        
        bool HasMoreThanOneLine(string conversation, Transform actor, Transform conversant)
        {
            var conversationModel = new ConversationModel(DialogueManager.masterDatabase, conversation, actor, conversant, false, null);
            var startNode = conversationModel.firstState;
            if (startNode.hasAnyResponses)
            {

                Debug.Log("no lines");
                return false; // The conversation has *no* lines.
            }
            else
            {
                var firstTextNode = conversationModel.GetState(startNode.firstNPCResponse.destinationEntry);
                if (firstTextNode.hasAnyResponses)
                {
                    Debug.Log("first line: " + firstTextNode.firstNPCResponse);
                    return true; // The conversation has more than one line.
                }
                else
                {
                    Debug.Log("first line: " + firstTextNode.firstNPCResponse);
                    return false; // The conversation has only one line.
                }
            }
        }

        protected override void DoConversationAction(Transform actor)
        {
            if (string.IsNullOrEmpty(conversation)) return;
            if (replace && DialogueManager.isConversationActive)
            {
                if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Stopping current active conversation " + DialogueManager.lastConversationStarted + " and starting " + conversation + ".", this);
                DialogueManager.StopConversation();
            }
            if (exclusive && DialogueManager.isConversationActive)
            {
                if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Conversation triggered on " + name + " but skipping because another conversation is active.", this);
            }
            else
            {
                var actorTransform = Tools.Select(conversationActor, actor);
                var conversantTransform = conversationConversant;
                if (conversantTransform == null)
                {
                    var conversationAsset = DialogueManager.MasterDatabase.GetConversation(conversation);
                    var conversationConversantActor = (conversationAsset != null) ? DialogueManager.MasterDatabase.GetActor(conversationAsset.ConversantID) : null;
                    var registeredTransform = (conversationConversantActor != null) ? CharacterInfo.GetRegisteredActorTransform(conversationConversantActor.Name) : null;
                    conversantTransform = (registeredTransform != null) ? registeredTransform : this.transform;
                }
                if (skipIfNoValidEntries && !DialogueManager.ConversationHasValidEntry(conversation, actorTransform, conversantTransform, startConversationEntryID))
                {
                    if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Conversation triggered on " + name + " but skipping because no entries are currently valid.", this);
                }
                else
                {

                    if (stopConversationIfTooFar || showCursorDuringConversation || pauseGameDuringConversation)
                    { // Trigger may not be on actor or conversant, so we need to hook into these events:
                        DialogueManager.instance.conversationStarted += OnConversationStartAnywhere;
                        DialogueManager.instance.conversationEnded += OnConversationEndAnywhere;
                    }

                    if (HasMoreThanOneLine(conversation, actorTransform, conversantTransform))
                    {
                        Debug.Log("Conversation has more than one line, start conversation");
                        DialogueManager.StartConversation(conversation, actorTransform, conversantTransform, startConversationEntryID);
                    }
                    else
                    {
                        Debug.Log("Conversation has only one line, barking");
                        DialogueManager.Bark(conversation, conversantTransform);
                    }
                   
                    earliestTimeToAllowTriggerExit = Time.time + MarginToAllowTriggerExit;
                    if (stopConversationIfTooFar)
                    {
                        monitorDistanceCoroutine = StartCoroutine(MonitorDistance(DialogueManager.currentActor));
                    }
                }
            }
        }


        // These methods run even if this DialogueSystemTrigger isn't on the actor or conversant.
        // They handle monitoring distance, showCursorDuringConversation and pauseGameDuringConversation.
        private void OnConversationStartAnywhere(Transform actor)
        {
            DialogueManager.instance.conversationStarted -= OnConversationStartAnywhere;
            if (showCursorDuringConversation)
            {
                wasCursorVisible = Cursor.visible;
                savedLockState = Cursor.lockState;
                StartCoroutine(ShowCursorAfterOneFrame());
            }
            if (pauseGameDuringConversation && string.Equals(DialogueManager.lastConversationStarted, conversation))
            {
                didIPause = true;
                preConversationTimeScale = Time.timeScale;
                Time.timeScale = 0;
            }
        }
        private void OnConversationEndAnywhere(Transform actor)
        {
            DialogueManager.instance.conversationEnded -= OnConversationEndAnywhere;
            StopMonitoringConversationDistance();
            if (showCursorDuringConversation)
            {
                Cursor.visible = wasCursorVisible;
                Cursor.lockState = savedLockState;
            }
            if (pauseGameDuringConversation && didIPause)
            {
                didIPause = false;
                Time.timeScale = preConversationTimeScale;
            }
        }



    }

}


Re: Show a different Dialogue UI if conversation has only one line

Posted: Fri Feb 25, 2022 3:57 pm
by Tony Li
Wow, I think I just broke a record for number of typos in a single code blurb. Sorry about that. I fixed the typos in the HasMoreThanOneLine() method in my post above.

Re: Show a different Dialogue UI if conversation has only one line

Posted: Sat Feb 26, 2022 9:46 am
by OddMan2
Thank you, now it correctly falls back to the bark ui.
i have to stop working on this things late at night, i always miss very obvious typos! :)

If i can make a suggestion, i think it would be nice to add this behavior as a checkbox on the default dialogue system trigger's start conversation action

here's the full code in case someone reads this and wants to use it

Code: Select all

using UnityEngine;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.Character;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Networking;
#endif

namespace PixelCrushers.DialogueSystem.OpsiveUCCSupport
{

    /// <summary>
    /// This is a Dialogue System Trigger that serves as a UCC Interactable target.
    /// </summary>
    public class CustomDialogueSystemTriggerInteractableTarget : DialogueSystemTriggerInteractableTarget, IInteractableTarget, IInteractableMessage
    {

        bool HasMoreThanOneLine(string conversation, Transform actor, Transform conversant)
        {
            var conversationModel = new ConversationModel(DialogueManager.masterDatabase, conversation, actor, conversant, false, null);
            var startNode = conversationModel.firstState;
            if (!startNode.hasAnyResponses)
            {
                return false; // The conversation has *no* lines.
            }
            else
            {
                var firstTextNode = conversationModel.GetState(startNode.firstNPCResponse.destinationEntry);
                if (firstTextNode.hasAnyResponses)
                {
                    return true; // The conversation has more than one line.
                }
                else
                {
                    return false; // The conversation has only one line.
                }
            }
        }

        protected override void DoConversationAction(Transform actor)
        {
            if (string.IsNullOrEmpty(conversation)) return;
            if (replace && DialogueManager.isConversationActive)
            {
                if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Stopping current active conversation " + DialogueManager.lastConversationStarted + " and starting " + conversation + ".", this);
                DialogueManager.StopConversation();
            }
            if (exclusive && DialogueManager.isConversationActive)
            {
                if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Conversation triggered on " + name + " but skipping because another conversation is active.", this);
            }
            else
            {
                var actorTransform = Tools.Select(conversationActor, actor);
                var conversantTransform = conversationConversant;
                if (conversantTransform == null)
                {
                    var conversationAsset = DialogueManager.MasterDatabase.GetConversation(conversation);
                    var conversationConversantActor = (conversationAsset != null) ? DialogueManager.MasterDatabase.GetActor(conversationAsset.ConversantID) : null;
                    var registeredTransform = (conversationConversantActor != null) ? CharacterInfo.GetRegisteredActorTransform(conversationConversantActor.Name) : null;
                    conversantTransform = (registeredTransform != null) ? registeredTransform : this.transform;
                }
                if (skipIfNoValidEntries && !DialogueManager.ConversationHasValidEntry(conversation, actorTransform, conversantTransform, startConversationEntryID))
                {
                    if (DialogueDebug.logInfo) Debug.Log("Dialogue System: Conversation triggered on " + name + " but skipping because no entries are currently valid.", this);
                }
                else
                {

                    if (stopConversationIfTooFar || showCursorDuringConversation || pauseGameDuringConversation)
                    { // Trigger may not be on actor or conversant, so we need to hook into these events:
                        DialogueManager.instance.conversationStarted += OnConversationStartAnywhere;
                        DialogueManager.instance.conversationEnded += OnConversationEndAnywhere;
                    }

                    if (HasMoreThanOneLine(conversation, actorTransform, conversantTransform))
                    {
                        Debug.Log("Conversation has more than one line, start conversation");
                        DialogueManager.StartConversation(conversation, actorTransform, conversantTransform, startConversationEntryID);
                    }
                    else
                    {
                        Debug.Log("Conversation has only one line, barking");
                        DialogueManager.Bark(conversation, conversantTransform);
                    }
                   
                    earliestTimeToAllowTriggerExit = Time.time + MarginToAllowTriggerExit;
                    if (stopConversationIfTooFar)
                    {
                        monitorDistanceCoroutine = StartCoroutine(MonitorDistance(DialogueManager.currentActor));
                    }
                }
            }
        }


        // These methods run even if this DialogueSystemTrigger isn't on the actor or conversant.
        // They handle monitoring distance, showCursorDuringConversation and pauseGameDuringConversation.
        private void OnConversationStartAnywhere(Transform actor)
        {
            DialogueManager.instance.conversationStarted -= OnConversationStartAnywhere;
            if (showCursorDuringConversation)
            {
                wasCursorVisible = Cursor.visible;
                savedLockState = Cursor.lockState;
                StartCoroutine(ShowCursorAfterOneFrame());
            }
            if (pauseGameDuringConversation && string.Equals(DialogueManager.lastConversationStarted, conversation))
            {
                didIPause = true;
                preConversationTimeScale = Time.timeScale;
                Time.timeScale = 0;
            }
        }
        private void OnConversationEndAnywhere(Transform actor)
        {
            DialogueManager.instance.conversationEnded -= OnConversationEndAnywhere;
            StopMonitoringConversationDistance();
            if (showCursorDuringConversation)
            {
                Cursor.visible = wasCursorVisible;
                Cursor.lockState = savedLockState;
            }
            if (pauseGameDuringConversation && didIPause)
            {
                didIPause = false;
                Time.timeScale = preConversationTimeScale;
            }
        }



    }

}

Re: Show a different Dialogue UI if conversation has only one line

Posted: Sat Feb 26, 2022 11:04 am
by Tony Li
Thanks for sharing the code! It may be a bit of an edge case for the basic Dialogue System Trigger, so I'll direct people here if anyone wants to do the same in the future.