/// <summary> /// Called after the current node or the index of the current dialogue entry has changed. /// </summary> /// <remarks> /// Trigger events according to the current game state and how it is changed /// </remarks> /// <param name="nodeChanged"></param> /// <param name="dialogueChanged"></param> /// <param name="firstEntryOfNode"></param> /// <param name="dialogueStepped"></param> private void UpdateGameState(bool nodeChanged, bool dialogueChanged, bool firstEntryOfNode, bool dialogueStepped) { // Debug.LogFormat("UpdateGameState begin {0} {1} {2}", stepNumFromLastCheckpoint, restrainCheckpoint, forceCheckpoint); if (nodeChanged) { // Debug.Log($"Nova: node changed to {currentNode.name}"); if (firstEntryOfNode) { EnsureCheckpoint(); // always get a checkpoint at the beginning of the node } NodeChanged?.Invoke(new NodeChangedData(currentNode.name)); } if (dialogueChanged) { this.RuntimeAssert(currentIndex >= 0 && currentIndex < currentNode.dialogueEntryCount, "Dialogue index out of range."); if (!firstEntryOfNode && dialogueStepped) { stepNumFromLastCheckpoint++; } var gameStateRestoreEntry = checkpointManager.IsReached(currentNode.name, currentIndex, variables.hash); if (gameStateRestoreEntry == null) { // Tell the checkpoint manager a new dialogue entry has been reached checkpointManager.SetReached(currentNode.name, currentIndex, variables, GetGameStateStepRestoreEntry()); } // Change states after creating or restoring from checkpoint if (shouldSaveRealCheckpoint) { stepNumFromLastCheckpoint = 0; } if (gameStateRestoreEntry != null) { this.RuntimeAssert(stepNumFromLastCheckpoint == gameStateRestoreEntry.stepNumFromLastCheckpoint, $"StepNumFromLastCheckpoint mismatch: {stepNumFromLastCheckpoint} {gameStateRestoreEntry.stepNumFromLastCheckpoint}"); this.RuntimeAssert(restrainCheckpoint == gameStateRestoreEntry.restrainCheckpointNum, $"RestrainCheckpointNum mismatch: {restrainCheckpoint} {gameStateRestoreEntry.restrainCheckpointNum}"); } if (checkpointRestrained) { restrainCheckpoint--; if (restrainCheckpoint == 1) { Debug.LogWarning($"Nova: restrainCheckpoint reaches 1"); } } // As the action for this dialogue will be re-run, it's fine to just reset forceCheckpoint to false forceCheckpoint = false; DialogueWillChange?.Invoke(); state = State.ActionRunning; lastVariablesHashBeforeAction = variables.hash; currentDialogueEntry = currentNode.GetDialogueEntryAt(currentIndex); currentDialogueEntry.ExecuteAction(); StartCoroutine(WaitActionEnd(gameStateRestoreEntry != null)); } // Debug.LogFormat("UpdateGameState end {0} {1} {2} {3}", stepNumFromLastCheckpoint, restrainCheckpoint, forceCheckpoint, currentDialogueEntry?.displayData.FormatNameDialogue()); }
/// <summary> /// Add a dialogue entry to the end of the dialogue entry list /// </summary> /// <param name="entry"></param> public void AddDialogueEntry(DialogueEntry entry) { CheckFreeze(); dialogueEntries.Add(entry); }
public static DialogueEntry[] ParseDialogueEntries(string[] dialogueEntryTexts) { var indexToCode = new string[dialogueEntryTexts.Length]; var indexToText = new string[dialogueEntryTexts.Length]; var indexToAdditionalActions = new Dictionary <int, string>(); for (int i = 0; i < dialogueEntryTexts.Length; ++i) { ParseText(dialogueEntryTexts[i], out string code, out string text); indexToCode[i] = code; indexToText[i] = text; if (!string.IsNullOrEmpty(code)) { GenerateAdditionalActions(code, out string preloadActions, out string unpreloadActions, out string forceCheckpointActions); CombineActions(indexToAdditionalActions, Math.Max(i - PreloadDialogueSteps, 0), preloadActions); CombineActions(indexToAdditionalActions, i, unpreloadActions); // The first entry of a node must have a real checkpoint, so no need to force here if (i > 0) { CombineActions(indexToAdditionalActions, i - 1, forceCheckpointActions); } } } var results = new DialogueEntry[dialogueEntryTexts.Length]; for (int i = 0; i < dialogueEntryTexts.Length; ++i) { string code = indexToCode[i]; string text = indexToText[i]; indexToAdditionalActions.TryGetValue(i, out string additionalActions); var m = Regex.Match(text, @"(.*?)(?:::|::)(.*)"); string characterName, dialogue; if (m.Success) { characterName = m.Groups[1].Value; dialogue = m.Groups[2].Value; } else { characterName = ""; dialogue = text; } LuaFunction action = null; string combinedCode = string.Format(ActionBeforeLazyBlock, characterName) + (code ?? "") + (additionalActions ?? "") + string.Format(ActionAfterLazyBlock, characterName); if (!string.IsNullOrEmpty(combinedCode)) { action = LuaRuntime.Instance.WrapClosure(combinedCode); if (action == null) { throw new Exceptions.ScriptParseException( $"Syntax error while parsing lazy execution block:\nText: {text}\nCode: {combinedCode}"); } } // TODO: there may be some grammar to set different internal and displayed character names results[i] = new DialogueEntry(characterName, characterName, dialogue, action); } return(results); }