using System.Collections; using System.Collections.Generic; using UnityEngine; using RPGCreationKit; using RPGCreationKit.DialogueSystem; using XNode; namespace RPGCreationKit.AI { public class AITalkative : AIStatus, IDialoguable { public bool dialogueSystemEnabled = true; public IDialoguable speakingTo; public AILookAt aiLookAt; public DialogueGraph currentDialogueGraph; [HideInInspector] public DialogueGraph previousDialogueGraph; public DialogueGraph defaultDialogueGraph; // Variables to update the NPC state [HideInInspector] public bool isInConversation = false; [HideInInspector] public bool isTalking = false; [HideInInspector] public bool isListening = false; // Setting this in the consequences will allow NPC to follow and speak to the player [HideInInspector] public bool mustSpeakToPlayer = false; [HideInInspector] public bool mustSpeakToEntity = false; public int speakerIndex = -1; IEnumerator dialogueCoroutine = null; public void ChangeDialogueGraph(DialogueGraph _newDialogueGraph) { previousDialogueGraph = currentDialogueGraph; currentDialogueGraph = _newDialogueGraph; } public void ChangeDialogueGraph(string newDialogueID) { DialogueGraph newDialogueGraph = DialoguesDatabase.GetItem(newDialogueID); previousDialogueGraph = currentDialogueGraph; currentDialogueGraph = newDialogueGraph; } [AIInvokable] public void SpeakToAI(string _speakToID) { // Get the RckAI if possible RckAI sTo = null; CellsSystem.CellInformation.TryToGetAI(_speakToID, out sTo); if(sTo != null) { IDialoguable[] ppl = { this, sTo }; DialogueLogic(ppl, currentDialogueGraph); } } [AIInvokable] public void SetSpeakerIndexToAI(string _rckAI, int _index) { // Get the RckAI if possible RckAI sTo = null; CellsSystem.CellInformation.TryToGetAI(_rckAI, out sTo); if (sTo != null) { sTo.SetSpeakerIndex(_index); } } public void DialogueLogic(IDialoguable[] entities, DialogueGraph dialogue) { dialogueCoroutine = (ExecuteDialogueNode(dialogue.GetEntryNode(), entities)); StartCoroutine(dialogueCoroutine); } [SerializeField] public IDialoguable[] entitiesTalkingWith; public Node currentNode; public IEnumerator ExecuteDialogueNode(Node _node, IDialoguable[] _entities) { // Trigger Events and Script DialogueNode globalNode = (DialogueNode)_node; RCKFunctions.ExecuteEvents(globalNode.events); if (!string.IsNullOrEmpty(globalNode.resultScript)) RCKFunctions.ExecuteScript(globalNode.resultScript); currentNode = _node; entitiesTalkingWith = _entities; for (int i = 0; i < _entities.Length; i++) { _entities[i].OnDialogueStarts(null); } if (_node is EntryNode) { EntryNode thisNode = (EntryNode)_node; dialogueCoroutine = (ExecuteDialogueNode(thisNode.GetOutputPort("firstNode").Connection.node, _entities)); StartCoroutine(dialogueCoroutine); } else if (_node is ConditionsNode) { ConditionsNode thisNode = (ConditionsNode)_node; bool conditionsResult = RCKFunctions.VerifyConditions(thisNode.conditions); if (conditionsResult) { dialogueCoroutine = (ExecuteDialogueNode(thisNode.GetOutputPort("resultTrue").Connection.node, _entities)); StartCoroutine(dialogueCoroutine); } else { dialogueCoroutine = (ExecuteDialogueNode(thisNode.GetOutputPort("resultFalse").Connection.node, _entities)); StartCoroutine(dialogueCoroutine); } } else if(_node is NPCDialogueLineNode) { NPCDialogueLineNode thisNode = (NPCDialogueLineNode)_node; int speakerID = -1; // reset anim for (int i = 0; i < _entities.Length; i++) { if (thisNode.speakerID == _entities[i].GetSpeakerIndex()) { speakerID = _entities[i].GetSpeakerIndex(); } _entities[i].GetAnimator().SetTrigger("CancelDialogueAnimation"); } for (int i = 0; i < _entities.Length; i++) { if (thisNode.speakerID == _entities[i].GetSpeakerIndex()) { // Set look at AILookAt lookAt = _entities[i].GetEntityGameObject().GetComponent(); if (lookAt != null) lookAt.ForceToLookAtTarget(_entities[thisNode.lookAtEntityID].GetEntityLookAtPos()); _entities[i].GetAnimator().ResetTrigger("CancelDialogueAnimation"); // We found who have to speak: _entities[i].OnSpeakALine(thisNode); // Play talking animation if choosen if (!string.IsNullOrEmpty(thisNode.dialogueAnimationStr)) { _entities[i].GetAnimator().CrossFade(thisNode.dialogueAnimationStr, .1f); } else _entities[i].GetAnimator().SetTrigger("CancelDialogueAnimation"); // if player is close enough if (!isInOfflineMode && !Player.RckPlayer.instance.isInConversation && Vector3.Distance(Player.RckPlayer.instance.transform.position, _entities[i].GetEntityGameObject().transform.position) <= RCKSettings.PLAYER_HEARS_NPC_DIALOGUES_DISTANCE) RCKFunctions.DisplayHeardLine(thisNode.line, thisNode.lineTime); // Play the audio if exists if (thisNode.audioClip != null) { _entities[i].PlayAudioClip(thisNode.audioClip); } // Check for the timing of the line bool outOfTime = false; float startTime = Time.time; // To wait until the line ends while (!outOfTime) { // If the time expires if (!thisNode.useLenghtOfClip) { if (Time.time - startTime >= thisNode.lineTime) outOfTime = true; } else { if (Time.time - startTime >= thisNode.audioClip.length) outOfTime = true; } yield return null; } _entities[i].StopAudio(); // Check what we have to do next switch (thisNode.afterLine) { case AfterLine.NPC_DialogueLine: // If we have to trigger another dialogue line, we just re-start this courutine with the new line dialogueCoroutine = (ExecuteDialogueNode(thisNode.GetOutputPort("nextLine").Connection.node, _entities)); StartCoroutine(dialogueCoroutine); yield break; case AfterLine.PlayerQuestions: /* // Play listening animation if choosen if (!string.IsNullOrEmpty(thisNode.dialogueAnimationListeningStr)) { speakingTo.GetAnimator().CrossFade(thisNode.dialogueAnimationListeningStr, .1f); } if (thisNode.removePreviousQuestions) { foreach (Transform t in uiManager.playerQuestionsContent) Destroy(t.gameObject); } else if (thisNode.questionsToRemove.Length > 0) { List toDelete = new List(); foreach (Transform t in uiManager.playerQuestionsContent) { for (int i = 0; i < thisNode.questionsToRemove.Length; i++) { if (t.GetComponent().question.qID == thisNode.questionsToRemove[i]) toDelete.Add(t.gameObject); } } foreach (GameObject go in toDelete) { Destroy(go); } } List questions = new List(); // Check if we have to add some new question for (int i = 0; i < thisNode.playerQuestions.Count; i++) { int tempPosition = thisNode.playerQuestions[i].Position; // In case, add them GameObject newQuestion = Instantiate(uiManager.playerQuestionPrefab, uiManager.playerQuestionsContent); if (tempPosition == 0) tempPosition = i; questions.Add(newQuestion.GetComponent()); newQuestion.GetComponent() .Init(thisNode.playerQuestions[i], speakingTo, (DialogueNode)thisNode.GetOutputPort("playerQuestions " + i).Connection.node, thisNode.playerQuestions[i].Question, thisNode.playerQuestions[i].DeleteAfterAnswer, tempPosition); newQuestion.GetComponent().SetSiblingIndex(tempPosition); } questions.Sort(SortByPosition); for (int i = 0; i < questions.Count; i++) { questions[i].GetComponent().SetSiblingIndex(questions[i].position); } // Set the UI to allow the player to ask questions uiManager.npc_Line.gameObject.SetActive(false); uiManager.playerQuestionsContainer.SetActive(true); // Reset the scroll bar of the PlayerQuestionScrollView if (uiManager.playerQuestionsContainer.GetComponentInChildren() != null) uiManager.playerQuestionsContainer.GetComponentInChildren().value = 1f; speakingTo.OnFinishedSpeakingALine(); // Sleect first for input system if gamepad if (RckInput.isUsingGamepad) questions[0].GetComponent