Move To AI Before Starting Dialogue

I’ve seen a few other topics on this, but ran into some issues implementing some of the changes. It’s probably an easy fix, but I’ve had one too many late nights and can’t quite figure it out. :stuck_out_tongue:

I started by implementing the changes detailed here: Dialogue only showing up within a certain distance? - Unity Courses / Ask - GameDev.tv

The line I ran into issue with was this one:

PlayerConversant.StartDialogueAction(dialogue, speaker);

Speaker does not exist in the current context (inside AIConversant), so I’m wondering if I’ve missed a step. I’ll post my scripts below:

The PlayerConversant.cs

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using RPG.Core;
using RPG.Movement;

namespace RPG.Dialogue
{
    public class PlayerConversant : MonoBehaviour, IAction
    {
        [SerializeField] string playerName;

        Dialogue currentDialogue;
        DialogueNode currentNode = null;
        AIConversant currentConversant = null;

        AIConversant targetConversant;

        bool isChoosing = false;

        public event Action onConversationUpdated;

        public void StartDialogueAction(Dialogue dialogue, AIConversant speaker)
        {
            Debug.Log($"StartDialogueAction {speaker}/{dialogue}");
            if (currentConversant != null && currentConversant == speaker) return;
            if (currentDialogue != null) Quit();
            if (dialogue == null)
            {
                return;
            }

            GetComponent<ActionScheduler>().StartAction(this);
            targetConversant = speaker;
            currentDialogue = dialogue;
        }

        private void Update()
        {
            if (targetConversant)
            {
                if (Vector3.Distance(targetConversant.transform.position, transform.position) > 3)
                {
                    GetComponent<Mover>().MoveTo(targetConversant.transform.position, 1);
                }
                else
                {
                    GetComponent<Mover>().Cancel();
                    StartDialogue();
                }
            }
        }

        public void StartDialogue()
        {
            currentConversant = targetConversant;
            targetConversant = null;
            currentNode = currentDialogue.GetRootNode();
            TriggerEnterAction();
            onConversationUpdated();
        }

        public void Quit()
        {
            currentDialogue = null;
            TriggerExitAction();
            currentNode = null;
            isChoosing = false;
            currentConversant = null;
            onConversationUpdated();
        }

        public bool IsActive()
        {
            return currentDialogue != null;
        }

        public bool IsChoosing()
        {
            return isChoosing;
        }

        public string GetText()
        {
            if (currentNode == null)
            {
                return "";
            }

            return currentNode.GetText();
        }

        public string GetCurrentConversantName()
        {
            if (isChoosing)
            {
                return playerName;
            }
            else
            {
                return currentConversant.GetName();
            }
        }

        public IEnumerable<DialogueNode> GetChoices()
        {
            return currentDialogue.GetPlayerChildren(currentNode);
        }

        public void SelectChoice(DialogueNode chosenNode)
        {
            currentNode = chosenNode;
            TriggerEnterAction();
            isChoosing = false;
            // Short Responses (As Shown On Button):
            Next();
            // Longer Response Event Trigger:
            // onConversationUpdated();
        }

        public void Next()
        {
            int numPlayerResponses = currentDialogue.GetPlayerChildren(currentNode).Count();
            if (numPlayerResponses > 0)
            {
                isChoosing = true;
                TriggerExitAction();
                onConversationUpdated();
                return;
            }

            DialogueNode[] children = currentDialogue.GetAIChildren(currentNode).ToArray();
            if (children.Length > 0)
            {
                int randomIndex = UnityEngine.Random.Range(0, children.Count());
                TriggerExitAction();
                currentNode = children[randomIndex];
                TriggerEnterAction();
                onConversationUpdated();
            }
            else
            {
                Quit();
            } 
        }

        public bool HasNext()
        {
            return currentDialogue.GetAllChildren(currentNode).Count() > 0;
        }

        private void TriggerEnterAction()
        {
            if (currentNode != null)
            {
                TriggerAction(currentNode.GetOnEnterAction());
            }
        }

        private void TriggerExitAction()
        {
            if (currentNode != null)
            {
                TriggerAction(currentNode.GetOnExitAction());
            }
        }

        private void TriggerAction(string action)
        {
            if (action == "") { return; }

            foreach (DialogueTrigger trigger in currentConversant.GetComponents<DialogueTrigger>())
            {
                trigger.Trigger(action);
            }
        }

        public void Cancel()
        {
            GetComponent<Mover>().Cancel();
            Quit();
        }
    }
}

The AIConversant

using UnityEngine;
using RPG.Control;
using RPG.Attributes;

namespace RPG.Dialogue
{
    public class AIConversant : MonoBehaviour, IRaycastable
    {
        [SerializeField] Dialogue dialogue = null;

        [SerializeField] string conversantName;

        public CursorType GetCursorType()
        {
            return CursorType.Dialogue;
        }

        public bool HandleRaycast(PlayerController callingController)
        {
            if (dialogue == null)
            {
                return false;
            }

            if (GetComponent<Health>().IsDead())
            {
                return false;
            }

            if (Input.GetMouseButtonDown(0))
            {
                callingController.GetComponent<PlayerConversant>().StartDialogueAction(dialogue, speaker);
            }
            return true;
        }

        public string GetName()
        {
            return conversantName;
        }
    }
}

Thank you for being such a great help throughout the project thus far!

You have to add the code in the second set in the post you referenced to PlayerConversant.cs

AIConversant targetConversant;

        public void StartDialogueAction(Dialogue dialogue, AIConversant speaker)
        {
            Debug.Log($"StartDialogueAction {speaker}/{dialogue}");
            if (currentConversant != null && currentConversant == speaker) return;
            if (currentDialogue != null) QuitDialogue();
            if (dialogue == null)
            {
                return;
            }

            GetComponent<ActionScheduler>().StartAction(this);
            targetConversant = speaker;
            currentDialogue = dialogue;
        }

        private void Update()
        {
            if (targetConversant)
            {
                if (Vector3.Distance(targetConversant.transform.position, transform.position) > 3)
                {
                    GetComponent<Mover>().MoveTo(targetConversant.transform.position);
                }
                else
                {
                    GetComponent<Mover>().Cancel();
                    StartDialogue();
                }
            }
        }

        private void StartDialogue()
        {
            currentConversant = targetConversant;
            targetConversant = null;
            var nodes = FilterOnCondition(currentDialogue.GetRootNodes()).ToArray();
            SetCurrentNode(nodes[Random.Range(0, nodes.Length)]);
            OnCurrentDialogueChanged?.Invoke();
        }

This treats the PlayerConversant like the Fighter component, with the player walking to the AIConversant until it’s within range, and then calling StartDialogue.

No dice. I had most of that entered already.

var nodes = FilterOnCondition(currentDialogue.GetRootNodes()).ToArray();
            SetCurrentNode(nodes[Random.Range(0, nodes.Length)]);

I can’t use these as I’m not up to FilterOnCondition yet, so I’ve gone with the suggestion from the other thread:

currentNode = currentDialogue.GetRootNode();

This now results in an “Object reference not set to an instance of an object” on that line though.

StartDialogue used to need an AIConversant of newConversant, and a dialogue of newDialogue

StartDialogue(AIConversant newConversant, Dialogue newDialogue)

but these differ from StartDialogueAction. Do I move these from StartDialouge into StartDialogueAction and rename the references as required? This is where the AIConversant was getting confused when implementing:

PlayerConversant.StartDialogueAction(dialogue, speaker);

as it has no reference to speaker.

Thanks!

Oh, I see now, I may have had the parameters backwards between the two classes.

With StartDialougeAction, the parameters are Dialogue, AIConversant, which means that you need to call

StartDialogueAction(dialogue, this); 

“this” is the very AIConversant that is calling the StartDialogueAction

That did the trick!

Thanks again,
Mark.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms