Example #1
0
        /// <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);
        }
Example #2
0
        /// <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));
            }
        }
Example #3
0
 /// <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);
 }
Example #4
0
 /// <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);
 }
Example #5
0
 private void MoveToNextNode(FlowChartNode nextNode, Action onFinish)
 {
     nodeHistory.Add(nextNode.name);
     currentNode  = nextNode;
     currentIndex = 0;
     UpdateGameState(true, true, true, true, false, onFinish);
 }
Example #6
0
 /// <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();
 }
Example #7
0
        /// <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.");
            }
        }
Example #8
0
        /// <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);
        }
Example #9
0
 /// <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();
 }
Example #10
0
        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();
        }
Example #11
0
        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);
        }
Example #12
0
 /// <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;
 }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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;
        }
Example #15
0
        /// <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;
        }
Example #16
0
        /// <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);
        }
Example #17
0
        /// <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());
            }
        }
Example #18
0
        /// <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);
        }
Example #19
0
        /// <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;
        }
Example #20
0
        /// <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;
        }
Example #21
0
        /// <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;
        }
Example #22
0
        /// <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;
        }
Example #23
0
        /// <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}");
            }
        }
Example #24
0
        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();
        }
Example #25
0
 /// <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;
 }
Example #26
0
 /// <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));
 }
Example #27
0
 /// <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);
 }
Example #28
0
 public LazyBindingEntry(FlowChartNode from, string destination, BranchInformation branchInfo)
 {
     this.from        = from;
     this.destination = destination;
     this.branchInfo  = branchInfo;
 }
Example #29
0
 public void AddUnlockedStart(string name, FlowChartNode node)
 {
     unlockedStartNodes.Add(name, node);
 }
Example #30
0
 /// <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);
 }