/// <summary> /// Get the name of the end node /// </summary> /// <param name="node">The end node</param> /// <returns> /// the name of the end if the node is an end node, null will been returned if the node is not an end. /// </returns> public string GetEndName(FlowChartNode node) { string name; var hasFound = endNodes.TryGetValue(node, out name); return(hasFound ? name : null); }
/// <summary> /// This method is designed to be called externally by scripts. /// A new flow chart node will be created and registered to the current constructing FlowChartTree. /// If current editing node is a normal node, the newly created one is intended to be its /// succeed node. The link between the new node and the current one will be added immediately, which /// won't be registered as a lazy binding link. /// </summary> /// <param name="name">the name of the new node</param> /// <param name="description">the description of the new node</param> public void RegisterNewNode(string name, string description) { var nextNode = new FlowChartNode(name, description); if (currentNode != null && currentNode.type == FlowChartNodeType.Normal) { currentNode.AddBranch(BranchInformation.Defualt, nextNode); } currentNode = nextNode; // The try block here is to make debug info easier to read try { flowChartTree.AddNode(currentNode); } catch (ArgumentNullException) { throw new ArgumentException("Nova: A label must have a name"); } catch (ArgumentException) { throw new DuplicatedDefinitionException( string.Format("Nova: Multiple definition of the same label {0}", currentNode.name)); } }
/// <summary> /// Move on to the next node /// </summary> /// <param name="nextNode">The next node to move to</param> private void MoveToNextNode(FlowChartNode nextNode) { walkedThroughNodes.Add(nextNode.name); currentNode = nextNode; currentIndex = 0; UpdateGameState(true, true, true, true); }
/// <summary> /// Start the game from the given node /// </summary> /// <param name="startNode">The node from where the game starts</param> private void GameStart(FlowChartNode startNode) { // clear possible history walkedThroughNodes = new List <string>(); state = State.Normal; MoveToNextNode(startNode); }
private void MoveToNextNode(FlowChartNode nextNode, Action onFinish) { nodeHistory.Add(nextNode.name); currentNode = nextNode; currentIndex = 0; UpdateGameState(true, true, true, true, false, onFinish); }
/// <summary> /// Move on to the next node /// </summary> /// <param name="nextNode">The next node to move to</param> private void MoveToNextNode(FlowChartNode nextNode) { Assert.IsFalse(walkedThroughNodes.Count != 0 && nextNode.name == walkedThroughNodes.Last()); walkedThroughNodes.Add(nextNode.name); currentIndex = 0; UpdateGameState(); }
/// <summary> /// Add an end node. /// </summary> /// <remarks> /// This method will check if the given node is already in the tree. it will raise an ArgumentException /// if the node is not found. A node can have only one end name, and an end name can refer to only one node. /// A DuplicatedDefinitionException will been raised of the above bijection rule is violated. /// </remarks> /// <param name="name"> /// the name of the end. the name of the end can be different from that of the node /// </param> /// <param name="node">the end node</param> /// <exception cref="ArgumentException"> /// An ArgumentException will be raised if the node if not in the tree /// </exception> public void AddEnd(string name, FlowChartNode node) { CheckFreeze(); if (!HasNode(node)) { throw new ArgumentException("Nova: Only node in the tree can be set as an end node."); } var existingNodeName = GetEndName(node); if (existingNodeName == null) { // This node has not been defined as an end if (endNodes.ContainsValue(name)) { // but the name has been used throw new DuplicatedDefinitionException( $"Nova: Duplicated definition of the same end name: {name}"); } // The name is legal, add end node endNodes.Add(node, name); return; } // This node has already been defined if (existingNodeName != name) { // But the end name of this node is not the same as the current one throw new DuplicatedDefinitionException( $"Nova: Assigning two different end name: {existingNodeName} and {name} to the same node."); } }
/// <summary> /// Add a node to the flow chart tree /// </summary> /// <param name="node"> /// The node to be added. No checking will be performed on the name of the node /// </param> public void AddNode(FlowChartNode node) { CheckFreeze(); var name = node.name; nodes.Add(name, node); }
/// <summary> /// Move on to the next node /// </summary> /// <param name="nextNode">The next node to move to</param> private void MoveToNode(FlowChartNode nextNode) { walkedThroughNodes.Add(nextNode.name); currentNode = nextNode; currentIndex = 0; NodeChanged.Invoke(new NodeChangedEventData(currentNode.name, currentNode.description)); UpdateGameState(); }
public void ForceInit(string path) { currentNode = null; stateLocale = I18n.DefaultLocale; lazyBindingLinks = new List <LazyBindingEntry>(); // requires.lua is executed and ScriptDialogueEntryParser.PatternToActionGenerator is filled before calling ParseScript() LuaRuntime.Instance.BindObject("scriptLoader", this); LuaRuntime.Instance.UpdateExecutionContext(new ExecutionContext(ExecutionMode.Eager, DialogueActionStage.Default, false)); InitOnlyIncludedNames(); flowChartTree.Unfreeze(); foreach (var locale in I18n.SupportedLocales) { stateLocale = locale; string localizedPath = path; if (locale != I18n.DefaultLocale) { localizedPath = I18n.LocalizedResourcesPath + locale + "/" + path; } var scripts = Resources.LoadAll(localizedPath, typeof(TextAsset)).Cast <TextAsset>(); foreach (var script in scripts) { if (onlyIncludedNames.Count > 0 && !onlyIncludedNames.Contains(script.name)) { continue; } #if UNITY_EDITOR var scriptPath = AssetDatabase.GetAssetPath(script); Debug.Log($"Nova: Parse script {scriptPath}"); #endif try { ParseScript(script); } catch (ParseException e) { throw new ParseException($"Failed to parse {script.name}", e); } } } // Bind all lazy binding entries BindAllLazyBindingEntries(); // Perform sanity check flowChartTree.SanityCheck(); // Construction finished, freeze the tree status flowChartTree.Freeze(); }
public void AddLocalizedNode(string name, string displayName) { currentNode = flowChartTree.GetNode(name); if (currentNode == null) { throw new ArgumentException($"Nova: Node {name} not found."); } currentNode.AddLocalizedName(stateLocale, displayName); }
/// <summary> /// Reset GameState, make it the same as the game not start /// </summary> /// <remarks> /// No event will be triggered when this method is called /// </remarks> public void ResetGameState() { if (CheckActionRunnig()) { return; } // Reset all walkedThroughNodes = null; currentIndex = 0; currentNode = null; oldIndex = -1; currentDialogueEntry = null; state = State.Normal; }
/// <summary> /// Create a new flow chart node register it to the current constructing FlowChartTree. /// If the current node is a normal node, the newly created one is intended to be its /// succeeding node. The link between the new node and the current one will be added immediately, which /// will not be registered as a lazy binding link. /// This method is designed to be called externally by scripts. /// </summary> /// <param name="name">Internal name of the new node</param> /// <param name="displayName">Displayed name of the new node</param> public void RegisterNewNode(string name, string displayName) { var nextNode = new FlowChartNode(name); if (currentNode != null && currentNode.type == FlowChartNodeType.Normal) { currentNode.AddBranch(BranchInformation.Default, nextNode); } currentNode = nextNode; flowChartTree.AddNode(currentNode); currentNode.AddLocalizedName(stateLocale, displayName); }
/// <summary> /// Add a node to the flow chart tree /// </summary> /// <param name="node">The node to add</param> /// <exception cref="ArgumentException"> /// ArgumentException will be thrown if the name is null or empty. /// </exception> public void AddNode(FlowChartNode node) { CheckFreeze(); if (string.IsNullOrEmpty(node.name)) { throw new ArgumentException("Nova: Node name is null or empty."); } if (nodes.ContainsKey(node.name)) { Debug.LogWarning($"Nova: Overwrite node: {node.name}"); } nodes[node.name] = node; }
/// <summary> /// Register a lazy binding link and null the current node. /// This method is designed to be called externally by scripts. /// </summary> /// <param name="destination">Destination of the jump</param> public void RegisterJump(string destination) { if (destination == null) { throw new ArgumentException( $"Nova: jump_to instruction must have a destination. Exception occurs at node: {currentNode.name}"); } if (currentNode.type == FlowChartNodeType.Branching) { throw new ArgumentException("Nova: Cannot apply jump_to() to a branching node."); } lazyBindingLinks.Add(new LazyBindingEntry(currentNode, destination, BranchInformation.Default)); currentNode = null; }
/// <summary> /// Add a start up node /// </summary> /// <remarks> /// This method will check if the given node is already in the tree. it will raise an ArgumentException /// if the node is not found. If the name has already been defined before, a DuplicatedDefinitionException /// will been thrown /// </remarks> /// <param name="name"> /// the name of the start up. the name of the starting point can be different from that of the node /// </param> /// <param name="node">the start up node</param> /// <exception cref="DuplicatedDefinitionException"> /// If the same name has been defined for multiple times, A DuplicatedDefinitionException will been thrown /// </exception> /// <exception cref="ArgumentException"> /// An ArgumentException will be raised if the node is not in the tree /// </exception> public void AddStartUp(string name, FlowChartNode node) { CheckFreeze(); if (!HasNode(node)) { throw new ArgumentException("Nova: Only node in the tree can be setted as a start up node"); } var existingStartNode = GetStartUpNode(name); if (existingStartNode != null && !existingStartNode.Equals(node)) { throw new DuplicatedDefinitionException( string.Format("Nova: duplicated definition of the same start up name: {0}", name)); } startUpNodes.Add(name, node); }
/// <summary> /// Called after the current node or the index of the current dialogue entry has changed. /// </summary> /// <remarks> /// The game state will be updated according to walkedThroughNodes and current dialogue index. /// This method will check if the game state has changed and trigger proper events /// </remarks> /// <param name="forceRefreshDialogue">refresh dialogue no matter the game state has change or not</param> /// <param name="forceRefreshNode">refresh the node no matter the node has changed or not</param> private void UpdateGameState(bool forceRefreshDialogue = false, bool forceRefreshNode = false) { Assert.IsFalse(walkedThroughNodes.Count == 0, "Nova: walkedThroughNodes is empty, can not update game state."); // update current node var desiredNodeName = walkedThroughNodes.Last(); var nodeChanged = currentNode == null || currentNode.name != desiredNodeName; if (nodeChanged || forceRefreshNode) { currentNode = flowChartTree.FindNode(desiredNodeName); if (NodeChanged != null) { NodeChanged.Invoke(new NodeChangedData(currentNode.name, currentNode.description)); } } // update dialogue var dialogueChanged = nodeChanged || currentIndex != oldIndex; if (dialogueChanged || forceRefreshDialogue) { Assert.IsTrue(currentIndex >= 0 && currentIndex < currentNode.DialogueEntryCount, "Nova: dialogue index out of range"); currentDialogueEntry = currentNode.GetDialogueEntryAt(currentIndex); oldIndex = currentIndex; if (checkpointManager.IsReached(currentNode.name, currentIndex) == null) { // tell the checkpoint manager a new dialogue entry has been reached checkpointManager.SetReached(currentNode.name, currentIndex, GetGameStateStepRestoreEntry()); } if (DialogueWillChange != null) { DialogueWillChange.Invoke(); } state = State.ActionRunning; currentDialogueEntry.ExecuteAction(); StartCoroutine(WaitActionEnd()); } }
/// <summary> /// Add a start node. /// </summary> /// <remarks> /// A name can be assigned to a start point, which can differ from the node name. /// The name should be unique among all start point names. /// This method will check if the given name is not in the tree, and the given node is already in the tree. /// </remarks> /// <param name="name">Name of the start point</param> /// <param name="node">The node to add</param> /// <exception cref="DuplicatedDefinitionException"> /// DuplicatedDefinitionException will be thrown if the same start point name has been defined. /// </exception> /// <exception cref="ArgumentException"> /// ArgumentException will be thrown if the node is not in the tree. /// </exception> public void AddStart(string name, FlowChartNode node) { CheckFreeze(); if (!HasNode(node)) { throw new ArgumentException("Nova: Only node in the tree can be set as a start node."); } var existingStartNode = GetStartNode(name); if (existingStartNode != null && !existingStartNode.Equals(node)) { throw new DuplicatedDefinitionException( $"Nova: Duplicated definition of the same start name: {name}"); } startNodes.Add(name, node); }
/// <summary> /// Add a start node. /// </summary> /// <remarks> /// A name can be assigned to a start point, which can differ from the node name. /// The name should be unique among all start point names. /// This method will check if the given name is not in the tree, and the given node is already in the tree. /// </remarks> /// <param name="name">Name of the start point</param> /// <param name="node">The node to add</param> /// <exception cref="ArgumentException"> /// ArgumentException will be thrown if the name is null or empty, or the node is not in the tree. /// </exception> public void AddStart(string name, FlowChartNode node) { CheckFreeze(); if (string.IsNullOrEmpty(name)) { throw new ArgumentException("Nova: Start name is null or empty."); } if (!HasNode(node)) { throw new ArgumentException("Nova: Only node in the tree can be set as a start node."); } if (startNodes.ContainsKey(name)) { Debug.LogWarning($"Nova: Overwrite start point: {name}"); } startNodes[name] = node; }
/// <summary> /// Set the current node as an end node /// This method is designed to be called externally by scripts. /// </summary> /// <remarks> /// While a flow chart can have multiple endings, each name of endings should be unique among other endings, /// and a node can only have one end name. /// </remarks> /// <param name="name">The name of the ending</param> /// <exception cref="ArgumentException"> /// ArgumentException will been thrown if is_end is called when the label is not defined. /// </exception> public void SetCurrentAsEnd(string name) { if (currentNode == null) { throw new ArgumentException( string.Format("Nova: is_end({0}) should be called after the definition of a label", name)); } // Set the current node type as end currentNode.type = FlowChartNodeType.End; // Add the node as an end if (name == null) { name = currentNode.name; } flowChartTree.AddEnd(name, currentNode); // null the current node, is_end will indicates the end of a label currentNode = null; }
/// <summary> /// Set the current node as an end node. /// This method is designed to be called externally by scripts. /// </summary> /// <remarks> /// A flow chart tree can have multiple end points. /// A name can be assigned to an end point, which can differ from the node name. /// The name should be unique among all end point names. /// </remarks> /// <param name="name"> /// Name of the end point. /// If no name is given, the name of the current node will be used. /// </param> /// <exception cref="ArgumentException"> /// ArgumentException will be thrown if called without registering the current node. /// </exception> public void SetCurrentAsEnd(string name) { if (currentNode == null) { throw new ArgumentException( $"Nova: SetCurrentAsEnd({name}) should be called after registering the current node."); } // Set the current node type as End currentNode.type = FlowChartNodeType.End; // Add the node as an end if (name == null) { name = currentNode.name; } flowChartTree.AddEnd(name, currentNode); // Null the current node, because SetCurrentAsEnd() indicates the end of a node currentNode = null; }
/// <summary> /// Register a lazy binding link and null the current node /// This method is designed to be called externally by scripts. /// </summary> /// <param name="destination">the destination of jump</param> public void RegisterJump(string destination) { if (destination == null) { var msg = "Nova: jump_to instruction must have a destination."; msg += " Exception occurs at chunk: " + currentNode.name; throw new ArgumentException(msg); } if (currentNode.type == FlowChartNodeType.Branching) { throw new ArgumentException("Nova: Can not apply 'jump_to' to a Branching node"); } lazyBindingLinks.Add(new LazyBindingEntry { from = currentNode, branchInfo = BranchInformation.Defualt, destination = destination }); currentNode = null; }
/// <summary> /// Add an end node. /// </summary> /// <remarks> /// A name can be assigned to an end point, which can differ from the node name. /// The name should be unique among all end point names. /// This method will check if the given name is not in the tree, and the given node is already in the tree. /// </remarks> /// <param name="name">Name of the end point</param> /// <param name="node">The node to add</param> /// <exception cref="DuplicatedDefinitionException"> /// DuplicatedDefinitionException will be thrown if assigning two different end names to the same node. /// </exception> /// <exception cref="ArgumentException"> /// ArgumentException will be thrown if the name is null or empty, or the node is not in the tree. /// </exception> public void AddEnd(string name, FlowChartNode node) { CheckFreeze(); if (string.IsNullOrEmpty(name)) { throw new ArgumentException("Nova: End name is null or empty."); } if (!HasNode(node)) { throw new ArgumentException("Nova: Only node in the tree can be set as an end node."); } var existingNodeName = GetEndName(node); if (existingNodeName == null) { // The node has not been defined as an end if (endNodes.ContainsValue(name)) { // But the name has been used Debug.LogWarning($"Nova: Overwrite end point: {name}"); } // The name is unique, add the node as en and endNodes[node] = name; return; } // The node has already been defined as an end if (existingNodeName != name) { // But the name of the end point is different throw new DuplicatedDefinitionException( $"Nova: Assigning two different end names to the same node: {existingNodeName} and {name}"); } }
public void ForceInit(string path) { flowChartTree = new FlowChartTree(); currentNode = null; stateLocale = I18n.DefaultLocale; lazyBindingLinks = new List <LazyBindingEntry>(); // requires.lua is executed and ScriptDialogueEntryParser.PatternToActionGenerator is filled before calling ParseScript() LuaRuntime.Instance.BindObject("scriptLoader", this); foreach (var locale in I18n.SupportedLocales) { stateLocale = locale; string localizedPath = path; if (locale != I18n.DefaultLocale) { localizedPath = I18n.LocalePath + locale + "/" + path; } var scripts = Resources.LoadAll(localizedPath, typeof(TextAsset)).Cast <TextAsset>().ToArray(); foreach (var script in scripts) { ParseScript(script.text); } } // Bind all lazy binding entries BindAllLazyBindingEntries(); // Perform sanity check flowChartTree.SanityCheck(); // Construction finished, freeze the tree status flowChartTree.Freeze(); }
/// <summary> /// Stop registering branch to the current node /// This method is designed to be called externally by scripts. /// Simply null the current node /// </summary> public void EndRegisterBranch() { currentNode = null; }
/// <summary> /// Check if the tree contains the given node /// </summary> /// <param name="node">the node to be found</param> /// <returns>true if the node has the given node</returns> public bool HasNode(FlowChartNode node) { return(nodes.ContainsKey(node.name)); }
/// <summary> /// Add a branch to this node /// </summary> /// <param name="branchInformation">The information of the branch</param> /// <param name="nextNode">The next node at this branch</param> public void AddBranch(BranchInformation branchInformation, FlowChartNode nextNode) { CheckFreeze(); branches.Add(branchInformation, nextNode); }
public LazyBindingEntry(FlowChartNode from, string destination, BranchInformation branchInfo) { this.from = from; this.destination = destination; this.branchInfo = branchInfo; }
public void AddUnlockedStart(string name, FlowChartNode node) { unlockedStartNodes.Add(name, node); }
/// <summary> /// Get the name of an end point /// </summary> /// <param name="node">The end node</param> /// <returns> /// The name of the end point if the node is an end node, otherwise return null /// </returns> public string GetEndName(FlowChartNode node) { return(endNodes.TryGetValue(node, out var name) ? name : null); }