示例#1
0
    /// <summary>
    /// Constructor for Quest Loading in the QuestLoader script.
    /// </summary>
    public Quest(QuestNodeCanvas questCanvas)
    {
        questName  = questCanvas.newQuest.questName;
        questGiver = questCanvas.newQuest.questGiver;

        objectives     = new List <List <QuestObjective> >();
        questDialogue  = null;
        turnInDialogue = null;

        // Parse concurrent objectives into our quest. Grab Objective Paths first and then sub objectives.
        foreach (ObjectivePath path in questCanvas.objectivePaths)
        {
            objectives.Add(new List <QuestObjective>());

            foreach (SubObjective subObjective in path.objectives)
            {
                if (subObjective.objective.tag == "Enemy")
                {
                    objectives[objectives.Count - 1].Add(new EnemyObjective(subObjective.objective.GetComponent <Enemy>(), subObjective.numberToCollect, questCanvas.newQuest.questGiver));
                }
                else if (subObjective.objective.tag == "Location")
                {
                    objectives[objectives.Count - 1].Add(new LocationObjective(subObjective.objective));
                }
                //else if(subObjective.objective.tag == "Item")
                //	objectives[objectives.Count - 1].Add(new ItemObjective(subObjective.objective.GetComponent<Item>()));
            }
        }

        // Set the first objective in each path to active.
        for (int i = 0; i < objectives.Count; i++)
        {
            objectives[i][0].SetActiveObjective();
        }

        // set up the linked list of next objectives
        foreach (List <QuestObjective> objectivePath in objectives)
        {
            for (int i = 0; i < objectivePath.Count - 1; i++)
            {
                objectivePath[i].SetNextObjective(objectivePath[i + 1]);
            }
        }

        requiredQuests = new List <string>();

        foreach (QuestAsset quest in questCanvas.requiredQuests)
        {
            requiredQuests.Add(quest.questName);
        }

        // Parse required game states
        requiredGameStates = new List <string>();

        foreach (GameStateName stateName in questCanvas.requiredGameStates)
        {
            requiredGameStates.Add(stateName.eventName);
        }
    }
示例#2
0
 /// <summary>
 /// Initializes all the quest data for this quest.
 /// </summary>
 /// <param name="newQuestName">The name of the quest.</param>
 /// <param name="newQuestGiver">The game object of the character who gives this quest.</param>
 /// <param name="newObjectives">The objectives for this quest.</param>
 /// <param name="newRequiredQuests">The prerequisite quests needed to unlock this quest.</param>
 /// <param name="newQuestDialogue">The dialogue exchange associated with this quest.</param>
 public void InitializeData(string newQuestName, GameObject newQuestGiver, List <QuestObjective> newObjectives, Dictionary <string, QuestAsset> newRequiredQuests, DialogueFSM newQuestDialogue)
 {
     questName      = newQuestName;
     questGiver     = newQuestGiver;
     objectives     = newObjectives;
     requiredQuests = newRequiredQuests;
     questDialogue  = newQuestDialogue;
 }
    /// <summary>
    /// Starts a new conversation.
    /// </summary>
    /// <param name="conversation">The conversation that is being started.</param>
    /// <param name="participants">The participants who take part in the conversation.</param>
    public void StartConversation(DialogueFSM conversation)
    {
        // First, check if this state has exceeded the number of times it can occur
        if (!conversation.CanOccur())
        {
            return;
        }

        // Next, check the probability and randomize to see if the conversation occurs.
        if (!conversation.DoesConversationOccur())
        {
            return;
        }

        // next, we need to iterate over the participants and make sure all of them are available for a conversation.
        // Note that each character will check if the necessary participants are in range of them. This means that this
        // function will only ever be called from a character who is in close proximity to each participant.
        if (!conversation.ContainsPlayer())
        {
            // Note that the player can interrupt a conversation to start a new one. NPC's, however, must go through this process first.
            foreach (KeyValuePair <string, GameObject> participant in conversation.GetParticipants())
            {
                if (!dialogueManagers[participant.Value].CanTalk())                 // if a participant is not available to talk, exit the function.
                {
                    return;
                }
            }
        }
        else if (conversation.ContainsPlayer() && !dialogueManagers[GameObject.FindWithTag("Player")].CanTalk())
        {
            return;
        }

        // If we get here, then we've confirmed all participants are available. Therefore, we can start the conversation.
        // Add a new conversation to the hashtable of active conversations.
        int newID = GenerateConversationID();         // Custom key is created for the conversation.

        activeConversations.Add(newID, conversation);

        // Now, we need to lock each participant out of other conversations.
        if (conversation.ContainsPlayer())
        {
            // Special case when the player is involved in the conversation.
            foreach (KeyValuePair <string, GameObject> participant in conversation.GetParticipants())
            {
                dialogueManagers[participant.Value].StartPlayerConversation(newID);
            }
        }
        else
        {
            foreach (KeyValuePair <string, GameObject> participant in conversation.GetParticipants())
            {
                dialogueManagers[participant.Value].StartConversation(newID);
            }
        }

        // Now we can execute our first print statement. If this state has a choice following it, we want to execute a different function to allow user choice.
        if (conversation.HasMultipleTransitions())
        {
            StartCoroutine(PrintDialogueWithChoices(newID));
        }
        else
        {
            PrintDialogue(newID);
        }
    }
    /// <summary>
    /// Parses the data from the FSM assets that have been created in the Dialogue Creator and creates
    /// DialogueFSM objects on the appropriate characters in the game.
    /// </summary>
    private void LoadDialogue()
    {
        Object[] fsms = Resources.LoadAll("Dialogue", typeof(DialogueNodeCanvas));         // load fsm's from Resources/Dialogue folder

        // Iterate over every dialogue FSM that is saved.
        for (int i = 0; i < fsms.Length; i++)
        {
            DialogueNodeCanvas fsm = (DialogueNodeCanvas)fsms[i];             // grab each Node Canvas Object, which holds all our states

            DialogueFSM newFSM = new DialogueFSM(fsm.numberOfTimesConversationCanHappen, fsm.probabilityOfOccurrence);

            string initiator = null;             // the character who initiates this fsm

            // Iterate over every node in the FSM
            foreach (Node node in fsm.nodes)
            {
                if (node.GetType() == typeof(DialogueNode))
                {
                    DialogueNode newNode = (DialogueNode)node;

                    if (newNode.isStartState)
                    {
                        // Assign the initiator to the player if the player initiation toggle has been set on the start state. Otherwise, set it to the npc's name.
                        initiator = newNode.playerInitiatesConversation ? GameObject.FindWithTag("Player").name : newNode.speakers[0].name;

                        if (!characters.ContainsKey(initiator))
                        {
                            return;                             // stop loading if the initiator doesn't exist.
                        }
                    }

                    // Separate the style sprite into a body and tail. Note that the body and tail sprites MUST have the same prefix to their names as the original style. For instance, if the original style is "NormalDialogue", then the body and tail must be "NormalDialogueBody" and "NormalDialogueTail".
                    List <Sprite> newBodyStyles = new List <Sprite>();
                    List <Sprite> newTailStyles = new List <Sprite>();

                    for (int j = 0; j < newNode.styles.Count; j++)
                    {
                        newBodyStyles.Add(Resources.Load <Sprite>("Sprites/Dialogue/" + newNode.styles[j].name + "Body"));
                        newTailStyles.Add(Resources.Load <Sprite>("Sprites/Dialogue/" + newNode.styles[j].name + "Tail"));
                    }

                    // Parse data from dialogue node. Note that we are using our characters hash table for the speaker. This might not be necessary, but this should ensure that we are grabbing the instances of the gameobjects that are IN the scene instead of in the editor. I'm not sure if there's a distinction between prefabs in the editor and instantiated prefabs in the scene.
                    List <GameObject> newSpeakerList = new List <GameObject>();

                    bool charactersExist = true;

                    foreach (GameObject character in newNode.speakers)
                    {
                        if (!characters.ContainsKey(character.name))
                        {
                            charactersExist = false;
                            break;
                        }

                        newSpeakerList.Add(characters[character.name]);
                        newFSM.AddParticipant(characters[character.name]);
                    }

                    // If the character is not in the scene, stop loading this FSM.
                    if (!charactersExist)
                    {
                        break;
                    }

                    newFSM.AddState(newSpeakerList, newNode.dialogues, newNode.description, newBodyStyles, newTailStyles, newNode.state);

                    // Add Transitions for non-choice nodes.
                    if (newNode.Outputs[0].connections.Count > 0)
                    {
                        // Ignore states that transition to a choice state
                        if (newNode.Outputs[0].connections[0].body.state > -2)
                        {
                            // Note that all states fire on the 0 input. However, choices fire on 1 - 9 inputs. Proceeding through dialogue always occurs using 0 input.
                            newFSM.AddTransition(newNode.state, newNode.Outputs[0].connections[0].body.state, 0);
                        }
                    }
                    else if (newNode.returnsToChoiceNode)
                    {
                        // Iterate over the previous choice node and create transitions for this state to go to any of the choice options on the previous choice node.
                        for (int j = 0; j < newNode.previousChoiceNode.Outputs.Count; j++)
                        {
                            if (newNode.previousChoiceNode.Outputs[j].connections.Count == 0)
                            {
                                continue;
                            }

                            newFSM.AddTransition(newNode.state, newNode.previousChoiceNode.Outputs[j].connections[0].body.state, j + 1);
                        }
                    }
                    else
                    {
                        // handle the final state case. A dialogue node with no output connection must be a final state so it transitions to -1.
                        newFSM.AddTransition(newNode.state, -1, 0);
                    }
                }
                else if (node.GetType() == typeof(DialogueChoiceNode))
                {
                    DialogueChoiceNode newNode = (DialogueChoiceNode)node;

                    // Parse transitions from choice node. Origin state is what leads us to the choice node. It is the state, all dialogue choices come from.
                    for (int j = 0; j < newNode.Outputs.Count; j++)
                    {
                        if (newNode.Outputs[j].connections.Count > 0)
                        {
                            newFSM.AddTransition(newNode.originState, newNode.Outputs[j].connections[0].body.state, j + 1);
                        }
                    }
                }
            }

            // Parse required quests
            List <string> tempRequiredStates = new List <string>();

            foreach (GameStateName stateName in fsm.requiredGameStates)
            {
                tempRequiredStates.Add(stateName.eventName);
            }

            newFSM.AddRequiredGameStates(tempRequiredStates);

            characters[initiator].GetComponent <DialogueManager>().AddFSM(newFSM);            // add the new FSM to the character's FSM list.
        }

        Destroy(gameObject);         // destroy the game object after it's finished loading all dialogue
    }
示例#5
0
    /// <summary>
    /// Parses the Dialogue FSM from the node editor and returns a DialgueFSM object.
    /// </summary>
    private DialogueFSM ParseDialogueFromQuest(QuestNodeCanvas questToParse, bool getTurnInQuestDialogue, bool getInProgressDialogue)
    {
        QuestNodeCanvas fsm = questToParse;             // grab each Node Canvas Object, which holds all our states

        DialogueFSM newFSM = new DialogueFSM(-1, 1.0f); // default values for quest dialogue. Probability is always 100% and can happen infinite number of times if the quest is available.

        string initiator = null;                        // the character who initiates this fsm

        // Iterate over every node in the FSM
        foreach (IQuestNode node in fsm.nodes)
        {
            // the boolean parameters act like switches. If we want a specific interaction, we want to ignore nodes that do not meet that interaction's criteria.
            if (!getTurnInQuestDialogue && !getInProgressDialogue && (node.IsTurnInDialogue() || node.IsInProgressDialogue()))
            {
                continue;
            }
            else if (getTurnInQuestDialogue && !node.IsTurnInDialogue())
            {
                continue;
            }
            else if (getInProgressDialogue && !node.IsInProgressDialogue())
            {
                continue;
            }

            if (node.GetType() == typeof(QuestDialogueNode))
            {
                QuestDialogueNode newNode = (QuestDialogueNode)node;

                if (newNode.isStartState)
                {
                    // Assign the initiator to the player if the player initiation toggle has been set on the start state. Otherwise, set it to the npc's name.
                    initiator = newNode.playerInitiatesConversation ? "Player" : newNode.speakers[0].name;
                    newFSM.SetStartState(newNode.state);
                }

                // Separate the style sprite into a body and tail. Note that the body and tail sprites MUST have the same prefix to their names as the original style. For instance, if the original style is "NormalDialogue", then the body and tail must be "NormalDialogueBody" and "NormalDialogueTail".
                List <Sprite> newBodyStyles = new List <Sprite>();
                List <Sprite> newTailStyles = new List <Sprite>();

                for (int j = 0; j < newNode.styles.Count; j++)
                {
                    newBodyStyles.Add(Resources.Load <Sprite>("Sprites/Dialogue/" + newNode.styles[j].name + "Body"));
                    newTailStyles.Add(Resources.Load <Sprite>("Sprites/Dialogue/" + newNode.styles[j].name + "Tail"));
                }

                // Parse data from dialogue node. Note that we are using our characters hash table for the speaker. This might not be necessary, but this should ensure that we are grabbing the instances of the gameobjects that are IN the scene instead of in the editor. I'm not sure if there's a distinction between prefabs in the editor and instantiated prefabs in the scene.
                List <GameObject> newSpeakerList = new List <GameObject>();

                foreach (GameObject character in newNode.speakers)
                {
                    newSpeakerList.Add(speakers[character.name]);
                    newFSM.AddParticipant(speakers[character.name]);
                }

                newFSM.AddQuestState(newSpeakerList, newNode.dialogues, newNode.description, newBodyStyles, newTailStyles, newNode.state, newNode.accept, newNode.reject);

                // Add Transitions for non-choice nodes.
                if (newNode.Outputs[0].connections.Count > 0)
                {
                    // Ignore states that transition to a choice state
                    if (newNode.Outputs[0].connections[0].body.state != -2)
                    {
                        // Note that all states fire on the 0 input. However, choices fire on 1 - 9 inputs. Proceeding through dialogue always occurs using 0 input.
                        newFSM.AddTransition(newNode.state, newNode.Outputs[0].connections[0].body.state, 0);
                    }
                }
                else if (newNode.returnsToChoiceNode)
                {
                    // Iterate over the previous choice node and create transitions for this state to go to any of the choice options on the previous choice node.
                    for (int j = 0; j < newNode.previousChoiceNode.Outputs.Count; j++)
                    {
                        if (newNode.previousChoiceNode.Outputs[j].connections.Count == 0)
                        {
                            continue;
                        }

                        newFSM.AddTransition(newNode.state, newNode.previousChoiceNode.Outputs[j].connections[0].body.state, j + 1);
                    }
                }
                else
                {
                    // handle the final state case. A dialogue node with no output connection must be a final state so it transitions to -1.
                    newFSM.AddTransition(newNode.state, -1, 0);
                }
            }
            else if (node.GetType() == typeof(QuestChoiceNode))
            {
                QuestChoiceNode newNode = (QuestChoiceNode)node;

                // Parse transitions from choice node. Origin state is what leads us to the choice node. It is the state, all dialogue choices come from.
                for (int j = 0; j < newNode.Outputs.Count; j++)
                {
                    if (newNode.Outputs[j].connections.Count > 0)
                    {
                        newFSM.AddTransition(newNode.originState, newNode.Outputs[j].connections[0].body.state, j + 1);
                    }
                }
            }
        }

        // Check and make sure states were added. No states may be added to the FSM if no turn in dialogue options were created.
        if (newFSM.GetStateCount() > 0)
        {
            return(newFSM);
        }
        else
        {
            return(null);
        }
    }
 /// <summary>
 /// Adds a new FSM to the list of FSM's for this character. Called on by DialogueLoader.
 /// </summary>
 /// <param name="newFSM">FSM that is being added.</param>
 public void AddFSM(DialogueFSM newFSM)
 {
     dialogueTrees.Add(newFSM);
 }
示例#7
0
 /// <summary>
 /// Adds an in-progress dialogue to this quest.
 /// </summary>
 /// <param name="newDialogue">The dialogue FSM to add.</param>
 public void AddInProgressDialogue(DialogueFSM newDialogue)
 {
     inProgressDialogue = newDialogue;
 }
示例#8
0
 /// <summary>
 /// Adds a turn-in dialogue to this quest.
 /// </summary>
 /// <param name="newDialogue">The dialogue FSM to add.</param>
 public void AddTurnInDialogue(DialogueFSM newDialogue)
 {
     turnInDialogue = newDialogue;
 }
示例#9
0
 /// <summary>
 /// Adds a Dialogue exchange to this quest.
 /// </summary>
 /// <param name="newDialogue">The dialogue exchange to add.</param>
 public void AddDialogue(DialogueFSM newDialogue)
 {
     questDialogue = newDialogue;
 }