private SpeechNode CreateSpeechNode(EditableSpeechNode editableNode) { SpeechNode speech = new SpeechNode(); speech.Name = editableNode.Name; speech.Text = editableNode.Text; speech.AutomaticallyAdvance = editableNode.AdvanceDialogueAutomatically; speech.AutoAdvanceShouldDisplayOption = editableNode.AutoAdvanceShouldDisplayOption; speech.TimeUntilAdvance = editableNode.TimeUntilAdvance; speech.TMPFont = editableNode.TMPFont; speech.Icon = editableNode.Icon; speech.Audio = editableNode.Audio; speech.Volume = editableNode.Volume; CopyParamActions(editableNode, speech); NodeEventHolder holder = this.GetNodeData(editableNode.ID); if (holder != null) { speech.Event = holder.Event; } return(speech); }
//-------------------------------------- // Construct User-Facing Conversation Object and Nodes //-------------------------------------- private Conversation ConstructConversationObject(EditableConversation ec) { // Create a conversation object Conversation conversation = new Conversation(); // Construct the parameters CreateParameters(ec, conversation); // Create a dictionary to store our created nodes by UID Dictionary <int, SpeechNode> speechByID = new Dictionary <int, SpeechNode>(); Dictionary <int, OptionNode> optionsByID = new Dictionary <int, OptionNode>(); // Create a Dialogue and Option node for each in the conversation // Put them in the dictionary for (int i = 0; i < ec.SpeechNodes.Count; i++) { SpeechNode node = CreateSpeechNode(ec.SpeechNodes[i]); speechByID.Add(ec.SpeechNodes[i].ID, node); } for (int i = 0; i < ec.Options.Count; i++) { OptionNode node = CreateOptionNode(ec.Options[i]); optionsByID.Add(ec.Options[i].ID, node); } // Now that we have every node in the dictionary, reconstruct the tree // And also look for the root ReconstructTree(ec, conversation, speechByID, optionsByID); return(conversation); }
private void CreateUIOptions() { // Display new options if (m_currentSpeech.ConnectionType == Connection.eConnectionType.Option) { for (int i = 0; i < m_currentSpeech.Connections.Count; i++) { OptionConnection connection = m_currentSpeech.Connections[i] as OptionConnection; if (ConditionsMet(connection)) { UIConversationButton uiOption = CreateButton(); uiOption.SetupButton(UIConversationButton.eButtonType.Option, connection.OptionNode); } } } // Display Continue/End options else { bool notAutoAdvance = !m_currentSpeech.AutomaticallyAdvance; bool allowVisibleOptionWithAuto = (m_currentSpeech.AutomaticallyAdvance && m_currentSpeech.AutoAdvanceShouldDisplayOption); if (notAutoAdvance || allowVisibleOptionWithAuto) { if (m_currentSpeech.ConnectionType == Connection.eConnectionType.Speech) { UIConversationButton uiOption = CreateButton(); SpeechNode next = GetValidSpeechOfNode(m_currentSpeech); // If there was no valid speech node (due to no conditions being met) this becomes a None button type if (next == null) { uiOption.SetupButton(UIConversationButton.eButtonType.End, null, endFont: m_conversation.EndConversationFont); } // Else, valid speech node found else { uiOption.SetupButton(UIConversationButton.eButtonType.Speech, next, continueFont: m_conversation.ContinueFont); } } else if (m_currentSpeech.ConnectionType == Connection.eConnectionType.None) { UIConversationButton uiOption = CreateButton(); uiOption.SetupButton(UIConversationButton.eButtonType.End, null, endFont: m_conversation.EndConversationFont); } } } SetSelectedOption(0); // Set the button sprite and alpha for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetImage(OptionImage, OptionImageSliced); m_uiOptions[i].SetAlpha(0); m_uiOptions[i].gameObject.SetActive(false); } }
//-------------------------------------- // Public functions //-------------------------------------- public void StartConversation(NPCConversation conversation) { m_conversation = conversation.Deserialize(); if (OnConversationStarted != null) { OnConversationStarted.Invoke(); } TurnOnUI(); m_currentSpeech = m_conversation.Root; SetState(eState.TransitioningDialogueBoxOn); }
public void StartConversationWithoutDeserialization(NPCConversation conversation) { m_conversation = conversation.RuntimeLoad(); if (OnConversationStarted != null) { OnConversationStarted.Invoke(); } TurnOnUI(); ClearOptions(); m_pendingDialogue = m_conversation.Root; SetState(eState.TransitioningDialogueBoxOn); }
//-------------------------------------- // Util //-------------------------------------- private bool IsAutoAdvance() { if (m_currentSpeech.ConnectionType == Connection.eConnectionType.Speech) { SpeechNode next = GetValidSpeechOfNode(m_currentSpeech); if (next != null) { SetupSpeech(next); return(true); } } else if (m_currentSpeech.ConnectionType == Connection.eConnectionType.None) { EndConversation(); return(true); } return(false); }
private void TransitionOptionsOff_Update() { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { ClearOptions(); if (m_currentSpeech.AutomaticallyAdvance) { if (IsAutoAdvance()) { return; } } if (m_selectedOption == null) { EndConversation(); return; } SpeechNode nextSpeech = GetValidSpeechOfNode(m_selectedOption); if (nextSpeech == null) { EndConversation(); } else { SetupSpeech(nextSpeech); } return; } for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetAlpha(1 - t); } SetColorAlpha(DialogueText, 1 - t); }
private void ReconstructTree(EditableConversation ec, Conversation conversation, Dictionary <int, SpeechNode> dialogues, Dictionary <int, OptionNode> options) { // Speech nodes List <EditableSpeechNode> editableSpeechNodes = ec.SpeechNodes; for (int i = 0; i < editableSpeechNodes.Count; i++) { EditableSpeechNode editableNode = editableSpeechNodes[i]; SpeechNode speechNode = dialogues[editableNode.ID]; // Connections List <EditableConnection> editableConnections = editableNode.Connections; for (int j = 0; j < editableConnections.Count; j++) { int childID = editableConnections[j].NodeUID; // Construct node->Speech if (editableConnections[j].ConnectionType == EditableConnection.eConnectiontype.Speech) { SpeechConnection connection = new SpeechConnection(dialogues[childID]); CopyConnectionConditions(editableConnections[j], connection); speechNode.Connections.Add(connection); } // Construct node->Option else if (editableConnections[j].ConnectionType == EditableConnection.eConnectiontype.Option) { OptionConnection connection = new OptionConnection(options[childID]); CopyConnectionConditions(editableConnections[j], connection); speechNode.Connections.Add(connection); } } // Root? if (editableNode.EditorInfo.isRoot) { conversation.Root = dialogues[editableNode.ID]; } } // Option nodes List <EditableOptionNode> editableOptionNodes = ec.Options; for (int i = 0; i < editableOptionNodes.Count; i++) { EditableOptionNode editableNode = editableOptionNodes[i]; OptionNode optionNode = options[editableNode.ID]; // Connections List <EditableConnection> editableConnections = editableNode.Connections; for (int j = 0; j < editableConnections.Count; j++) { int childID = editableConnections[j].NodeUID; // Construct node->Speech if (editableConnections[j].ConnectionType == EditableConnection.eConnectiontype.Speech) { SpeechConnection connection = new SpeechConnection(dialogues[childID]); CopyConnectionConditions(editableConnections[j], connection); optionNode.Connections.Add(connection); } } } }
public SpeechConnection(SpeechNode node) { SpeechNode = node; }
//-------------------------------------- // Do Speech //-------------------------------------- public void DoSpeech(SpeechNode speech) { if (speech == null) { EndConversation(); return; } m_currentSpeech = speech; // Clear current options ClearOptions(); m_currentSelectedIndex = -1; // Set sprite if (speech.Icon == null) { NpcIcon.sprite = BlankSprite; } else { NpcIcon.sprite = speech.Icon; } // Set font if (speech.TMPFont != null) { DialogueText.font = speech.TMPFont; } else { DialogueText.font = null; } // Set name NameText.text = speech.Name; // Set text if (string.IsNullOrEmpty(speech.Text)) { if (ScrollText) { DialogueText.text = ""; m_targetScrollTextCount = 0; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = ""; DialogueText.maxVisibleCharacters = 1; } } else { if (ScrollText) { DialogueText.text = speech.Text; m_targetScrollTextCount = speech.Text.Length + 1; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = speech.Text; DialogueText.maxVisibleCharacters = speech.Text.Length; } //if (Backlog != null) //{ // Backlog.text += speech.Name + ":\n" + speech.Text + "\n\n"; //} } // Call the event //speech.Event?.Invoke(); // Play the audio if (speech.Audio != null) { AudioPlayer.clip = speech.Audio; AudioPlayer.volume = speech.Volume; AudioPlayer.Play(); } // Display new options if (speech.Options.Count > 0) { //StartCoroutine(ShrinkDialogue()); // Decrease spacing when many options are present to prevent options being too small var layout = OptionsPanel.GetComponent <VerticalLayoutGroup>(); if (speech.Options.Count > 3) { layout.spacing = 10; } else { layout.spacing = 20; } for (int i = 0; i < speech.Options.Count; i++) { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.InitButton(speech.Options[i]); option.SetOption(speech.Options[i]); m_uiOptions.Add(option); } } else { //StartCoroutine(ExpandDialogue()); // Display "Continue" / "End" if we should. //bool notAutoAdvance = !speech.AutomaticallyAdvance; bool autoWithOption = (speech.AutomaticallyAdvance && speech.AutoAdvanceShouldDisplayOption); if (autoWithOption) { // Else display "continue" button to go to following dialogue if (speech.Dialogue != null) { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.SetFollowingAction(speech.Dialogue); m_uiOptions.Add(option); } // Else display "end" button else { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.SetAsEndConversation(); m_uiOptions.Add(option); } } } // Auto select first option // SetSelectedOption(0); // Set the button sprite and alpha for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetImage(OptionImage, OptionImageSliced); m_uiOptions[i].SetAlpha(0); m_uiOptions[i].gameObject.SetActive(false); } SetState(eState.ScrollingText); }
public void TurnOffPortraitAndPauseConversation() { m_pendingDialogue = m_currentSpeech.Dialogue; NpcIcon.gameObject.SetActive(false); SetState(eState.freeze); }
//-------------------------------------- // (CUSTOM) Pause/Continue Conversation //-------------------------------------- public void PauseConversation() { m_pendingDialogue = m_currentSpeech.Dialogue; SetState(eState.TransitioningDialogueOff); }
//-------------------------------------- // Update //-------------------------------------- private void Update() { // Use method to compute if we should progress or not bool progressInput = Progress(); if (m_state != eState.Off) { if (Input.GetKeyDown(KeyCode.LeftControl)) { skipping = !skipping; } } //if (Input.mouseScrollDelta.y > 0.2f && !BacklogGameObject.activeSelf) { // ToggleBacklog(); //} else if (Input.mouseScrollDelta.y < -0.2f && BacklogGameObject.activeSelf) { // ToggleBacklog(); //} switch (m_state) { case eState.TransitioningDialogueBoxOn: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { DoSpeech(m_pendingDialogue); return; } SetColorAlpha(DialogueBackground, t); SetColorAlpha(NpcIcon, t); SetColorAlpha(NameText, t); } break; case eState.ScrollingText: UpdateScrollingText(); break; case eState.TransitioningOptionsOn: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { SetState(eState.Idle); return; } for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetAlpha(t); } } break; case eState.Idle: { m_stateTime += Time.deltaTime; if (m_currentSpeech.Dialogue != null || m_currentSpeech.Options == null || m_currentSpeech.Options.Count == 0) { if (m_stateTime > m_currentSpeech.TimeUntilAdvance) { if ((progressInput || skipping || m_currentSpeech.AutomaticallyAdvance) && !isFrozen) { SetState(eState.TransitioningOptionsOff); } } } } break; case eState.TransitioningOptionsOff: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { ClearOptions(); m_currentSpeech.Event?.Invoke(); //if (m_currentSpeech.AutomaticallyAdvance) //{ if (m_currentSpeech.Dialogue != null) { DoSpeech(m_currentSpeech.Dialogue); return; } else if (m_currentSpeech.Options == null || m_currentSpeech.Options.Count == 0) { EndConversation(); return; } //} if (m_selectedOption == null) { EndConversation(); return; } SpeechNode nextAction = m_selectedOption.Dialogue; if (nextAction == null) { EndConversation(); } else { DoSpeech(nextAction); } return; } for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetAlpha(1 - t); } SetColorAlpha(DialogueText, 1 - t); } break; case eState.TransitioningDialogueOff: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { TurnOffUI(); return; } SetColorAlpha(DialogueBackground, 1 - t); SetColorAlpha(NpcIcon, 1 - t); SetColorAlpha(NameText, 1 - t); } break; case eState.freeze: break; } }
public void SetFollowingAction(SpeechNode action) { m_action = action; TextMesh.text = "Continue."; }
//-------------------------------------- // Do Speech //-------------------------------------- private void SetupSpeech(SpeechNode speech) { if (speech == null) { EndConversation(); return; } m_currentSpeech = speech; // Clear current options ClearOptions(); m_currentSelectedIndex = 0; // Set sprite if (speech.Icon == null) { NpcIcon.sprite = BlankSprite; } else { NpcIcon.sprite = speech.Icon; } // Set font if (speech.TMPFont != null) { DialogueText.font = speech.TMPFont; } else { DialogueText.font = null; } // Set name NameText.text = speech.Name; // Set text if (string.IsNullOrEmpty(speech.Text)) { if (ScrollText) { DialogueText.text = ""; m_targetScrollTextCount = 0; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = ""; DialogueText.maxVisibleCharacters = 1; } } else { if (ScrollText) { DialogueText.text = speech.Text; m_targetScrollTextCount = speech.Text.Length + 1; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = speech.Text; DialogueText.maxVisibleCharacters = speech.Text.Length; } } // Call the event if (speech.Event != null) { speech.Event.Invoke(); } DoParamAction(speech); // Play the audio if (speech.Audio != null) { AudioPlayer.clip = speech.Audio; AudioPlayer.volume = speech.Volume; AudioPlayer.Play(); } if (ScrollText) { SetState(eState.ScrollingText); } else { SetState(eState.TransitioningOptionsOn); } }
//-------------------------------------- // Option Selected //-------------------------------------- public void SpeechSelected(SpeechNode speech) { SetupSpeech(speech); }
//-------------------------------------- // Update //-------------------------------------- private void Update() { switch (m_state) { case eState.TransitioningDialogueBoxOn: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { DoSpeech(m_pendingDialogue); return; } SetColorAlpha(DialogueBackground, t); SetColorAlpha(NpcIcon, t); SetColorAlpha(NameText, t); } break; case eState.ScrollingText: UpdateScrollingText(); break; case eState.TransitioningOptionsOn: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { SetState(eState.Idle); return; } for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetAlpha(t); } } break; case eState.Idle: { m_stateTime += Time.deltaTime; if (m_currentSpeech.AutomaticallyAdvance) { if (m_currentSpeech.Dialogue != null || m_currentSpeech.Options == null || m_currentSpeech.Options.Count == 0) { if (m_stateTime > m_currentSpeech.TimeUntilAdvance) { SetState(eState.TransitioningOptionsOff); } } } } break; case eState.TransitioningOptionsOff: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { ClearOptions(); if (m_currentSpeech.AutomaticallyAdvance) { if (m_currentSpeech.Dialogue != null) { DoSpeech(m_currentSpeech.Dialogue); return; } else if (m_currentSpeech.Options == null || m_currentSpeech.Options.Count == 0) { EndConversation(); return; } } if (m_selectedOption == null) { EndConversation(); return; } SpeechNode nextAction = m_selectedOption.Dialogue; if (nextAction == null) { EndConversation(); } else { DoSpeech(nextAction); } return; } for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetAlpha(1 - t); } SetColorAlpha(DialogueText, 1 - t); } break; case eState.TransitioningDialogueOff: { m_stateTime += Time.deltaTime; float t = m_stateTime / TRANSITION_TIME; if (t > 1) { TurnOffUI(); return; } SetColorAlpha(DialogueBackground, 1 - t); SetColorAlpha(NpcIcon, 1 - t); SetColorAlpha(NameText, 1 - t); } break; } }
//-------------------------------------- // Do Speech //-------------------------------------- public void DoSpeech(SpeechNode speech) { if (speech == null) { EndConversation(); return; } m_currentSpeech = speech; // Clear current options ClearOptions(); m_currentSelectedIndex = 0; // Set sprite if (speech.Icon == null) { NpcIcon.sprite = BlankSprite; } else { NpcIcon.sprite = speech.Icon; } // Set font if (speech.TMPFont != null) { DialogueText.font = speech.TMPFont; } else { DialogueText.font = null; } // Set name NameText.text = speech.Name; // Set text if (string.IsNullOrEmpty(speech.Text)) { if (ScrollText) { DialogueText.text = ""; m_targetScrollTextCount = 0; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = ""; DialogueText.maxVisibleCharacters = 1; } } else { if (ScrollText) { DialogueText.text = speech.Text; m_targetScrollTextCount = speech.Text.Length + 1; DialogueText.maxVisibleCharacters = 0; m_elapsedScrollTime = 0f; m_scrollIndex = 0; } else { DialogueText.text = speech.Text; DialogueText.maxVisibleCharacters = speech.Text.Length; } } // Call the event if (speech.Event != null) { speech.Event.Invoke(); } // Play the audio if (speech.Audio != null) { AudioPlayer.clip = speech.Audio; AudioPlayer.volume = speech.Volume; AudioPlayer.Play(); } // Display new options if (speech.Options.Count > 0) { for (int i = 0; i < speech.Options.Count; i++) { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.InitButton(speech.Options[i]); option.SetOption(speech.Options[i]); m_uiOptions.Add(option); } } else { // Display "Continue" / "End" if we should. bool notAutoAdvance = !speech.AutomaticallyAdvance; bool autoWithOption = (speech.AutomaticallyAdvance && speech.AutoAdvanceShouldDisplayOption); if (notAutoAdvance || autoWithOption) { // Else display "continue" button to go to following dialogue if (speech.Dialogue != null) { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.SetFollowingAction(speech.Dialogue); m_uiOptions.Add(option); } // Else display "end" button else { UIConversationButton option = GameObject.Instantiate(ButtonPrefab, OptionsPanel); option.SetAsEndConversation(); m_uiOptions.Add(option); } } } SetSelectedOption(0); // Set the button sprite and alpha for (int i = 0; i < m_uiOptions.Count; i++) { m_uiOptions[i].SetImage(OptionImage, OptionImageSliced); m_uiOptions[i].SetAlpha(0); m_uiOptions[i].gameObject.SetActive(false); } SetState(eState.ScrollingText); }
public Conversation Deserialize() { // Deserialize an editor-version (containing all info) that // we will use to construct the user-facing Conversation data structure. EditableConversation ec = this.DeserializeForEditor(); // Create a conversation. Conversation conversation = new Conversation(); // Create a dictionary to store our created nodes by UID Dictionary <int, SpeechNode> dialogues = new Dictionary <int, SpeechNode>(); Dictionary <int, OptionNode> options = new Dictionary <int, OptionNode>(); // Create a Dialogue and Option node for each in the conversation // Put them in the dictionary for (int i = 0; i < ec.SpeechNodes.Count; i++) { SpeechNode node = new SpeechNode(); node.Name = ec.SpeechNodes[i].Name; node.Text = ec.SpeechNodes[i].Text; node.AutomaticallyAdvance = ec.SpeechNodes[i].AdvanceDialogueAutomatically; node.AutoAdvanceShouldDisplayOption = ec.SpeechNodes[i].AutoAdvanceShouldDisplayOption; node.TimeUntilAdvance = ec.SpeechNodes[i].TimeUntilAdvance; node.TMPFont = ec.SpeechNodes[i].TMPFont; node.Icon = ec.SpeechNodes[i].Icon; node.Audio = ec.SpeechNodes[i].Audio; node.Volume = ec.SpeechNodes[i].Volume; node.Options = new List <OptionNode>(); if (this.GetNodeData(ec.SpeechNodes[i].ID) != null) { node.Event = this.GetNodeData(ec.SpeechNodes[i].ID).Event; } dialogues.Add(ec.SpeechNodes[i].ID, node); } for (int i = 0; i < ec.Options.Count; i++) { OptionNode node = new OptionNode(); node.Text = ec.Options[i].Text; node.TMPFont = ec.Options[i].TMPFont; options.Add(ec.Options[i].ID, node); } // Now that we have every node in the dictionary, reconstruct the tree // And also look for the root for (int i = 0; i < ec.SpeechNodes.Count; i++) { // Connect dialogue to options for (int j = 0; j < ec.SpeechNodes[i].OptionUIDs.Count; j++) { dialogues[ec.SpeechNodes[i].ID].Options.Add(options[ec.SpeechNodes[i].OptionUIDs[j]]); } // Connect dialogue to following dialogue if (ec.SpeechNodes[i].SpeechUID != EditableConversation.INVALID_UID) { dialogues[ec.SpeechNodes[i].ID].Dialogue = dialogues[ec.SpeechNodes[i].SpeechUID]; } // Check if root if (ec.SpeechNodes[i].EditorInfo.isRoot) { conversation.Root = dialogues[ec.SpeechNodes[i].ID]; } } for (int i = 0; i < ec.Options.Count; i++) { // Connect option to following dialogue if (dialogues.ContainsKey(ec.Options[i].SpeechUID)) { options[ec.Options[i].ID].Dialogue = dialogues[ec.Options[i].SpeechUID]; } } return(conversation); }