Quest states

Announcements, support questions, and discussion for the Dialogue System.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

I'm getting the same errors I was getting when I tried myself. That is, that the variables in the Custom editor have already been declared.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CustomQuestStateListener) questName

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CustomQuestStateListener) questStateIndicatorLevels

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CustomQuestStateListener) questEntryStateIndicatorLevels

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(CustomQuestStateListener) m_questStateDispatcher

etc, etc.

and I did delete the files I had made.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

nvm it looks like for some reason the console hadn't refreshed. It just ran fine.
User avatar
Tony Li
Posts: 22055
Joined: Thu Jul 18, 2013 1:27 pm

Re: Quest states

Post by Tony Li »

Great! And yes, keep the CustomQuestStateCode script, or something similar, on the Dialogue Manager. You can get rid of the test code in it. You just need the top part, where commented.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

Yay! it works! Thank you so much. I can't be the first person who wanted this? As far as I remember (I never have time to actually play games now) most RPGs seem to work this way. Otherwise, you'd finish the quest before turning it back in to the quest giver.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

I'm definitely going to rework my inventory and item system some though for the next game... right now I have the quest item picked up in my item script so it checks if it's need for a quest when you add it to inventory. Which is a good way to save resources but it seems clunky and I have to add all kinds of arrays in case it's needed for multiple quests etc.
User avatar
Tony Li
Posts: 22055
Joined: Thu Jul 18, 2013 1:27 pm

Re: Quest states

Post by Tony Li »

nivlekius wrote: Sun Mar 17, 2019 5:03 pmYay! it works! Thank you so much. I can't be the first person who wanted this? As far as I remember (I never have time to actually play games now) most RPGs seem to work this way. Otherwise, you'd finish the quest before turning it back in to the quest giver.
It varies by game. In some, I've seen that the designer doesn't show a "preempted" indicator. Instead, the conversation simply checks if the quest conditions are met. If so, it goes down an entirely different branch ("I was going to ask you to do X, but I see you already did it! Here's a reward."). I may clean up the example above at some point and put it on the Extras page in case others want to use it.
nivlekius wrote: Sun Mar 17, 2019 5:07 pmI'm definitely going to rework my inventory and item system some though for the next game... right now I have the quest item picked up in my item script so it checks if it's need for a quest when you add it to inventory. Which is a good way to save resources but it seems clunky and I have to add all kinds of arrays in case it's needed for multiple quests etc.
Another suggestion, but a long read, in case you're interested:
Spoiler
This is one of the areas where Quest Machine improves on the way the Dialogue System handles quests. Quest Machine quests can listen for messages such as "Picked Up" + "Sword" and automatically advance the quest state. Your item script could send that message. If nothing's listening for the message, it's just ignored. If multiple quests are listening, they can each do something, without having to track them in a clunky array.

The good news is that you can do a very similar thing in the Dialogue System, which has access to the same MessageSystem. Below is one way. It may look a little complicated at first, but when you get it implemented it actually offloads a lot of work that you might otherwise have to write C# code for.

The idea is that each quest listens for a message such as "Picked Up" + "Sword" and runs some code that might advance the quest state. You define the message and the code (Lua code) all inside the Dialogue Editor, without having to write any C# code specific to the quest. Here's how you might implement it.

1. In your quest, add three fields:
  • Message: (Text) A message to listen for.
  • Parameter: (Text) Message parameter to listen for.
  • MessageAction: (Text) Lua code to run when it hears this message+parameter. It can update quest states, etc.
For example:
  • Name: Pick 3 Apples
  • Description: Pick 3 apples to bring to the baker.
  • Entry 1: [var=apples]/3 Apples
  • Message: Picked
  • Parameter: Apple
  • MessageAction:

    Code: Select all

    Variable["apples"] = Variable["apples"] + 1
    UpdateTracker()
    if (CurrentQuestState("Pick 3 Apples") == "active" and Variable["apples"] >= 3) then 
        SetQuestState("Pick 3 Apples", "success")
        ShowAlert("Got 3 apples! Good job!")
    end
2. In your item script, send the message:

Code: Select all

PixelCrushers.MessageSystem.SendMessage(this, "Picked", "Apple");
3. Add a script to the Dialogue Manager that listens for the quest messages. Something like:

Code: Select all

using UnityEngine;
using PixelCrushers;
using PixelCrushers.DialogueSystem;

public class QuestMessageListener : MonoBehaviour, IMessageHandler
{
    void Start() // Register for quest messages for all quests in database.
    {
        foreach (var quest in DialogueManager.masterDatabase.items)
        {
            if (quest.IsItem) continue; // Not a quest; skip it.
            var message = quest.LookupValue("Message");
            var parameter = quest.LookupValue("Parameter");
            if (string.IsNullOrEmpty(message)) continue; // No message; skip it.
            MessageSystem.AddListener(this, message, parameter);
        }
    }
    
    void OnMessage(MessageArgs messageArgs) // Handle quest messages.
    {
        foreach (var quest in DialogueManager.masterDatabase.items)
        {
            if (quest.IsItem) continue; // Not a quest; skip it.
            var message = quest.LookupValue("Message");
            var parameter = quest.LookupValue("Parameter");
            if (messageArgs.Matches(message, parameter)) // Message is for this quest.
            {
                Lua.Run(quest.LookupValue("MessageAction"));
            }
        }
    }
}
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

I'm definitely going to give that a go. And let me say I'm sorry before I even tell you this, but I still have the problem of the quest journal not showing the quest. There's no option to show it when the quest in "preempted" My thinking is that having in the journal until the item is turned in reminds people to go back to turn it in and not to destroy what would normally be a useless object. And in this specific case Chazpea (aka. Chuck Pegano :D ) gives a lot of information once he gets the skull. I'd really like for the quest to stay in the journal until complete is set.
User avatar
Tony Li
Posts: 22055
Joined: Thu Jul 18, 2013 1:27 pm

Re: Quest states

Post by Tony Li »

Sorry, I didn't realize you wanted to show preempted quests in the journal. It's possible. Make a subclass of StandardUIQuestLogWindow. Override the ShowQuests method. Something like:

Code: Select all

using System.Collections.Generic;
using PixelCrushers.DialogueSystem;

public class CustomQuestLogWindow : StandardUIQuestLogWindow
{

    public override void Awake()
    {
        base.Awake();
        QuestLog.StringToState = CustomStringToState;
    }

    public static QuestState CustomStringToState(string s)
    {
        // Treat "preempted" the same as "unassigned":
        if (s == "preempted") return QuestState.Unassigned;
        else return QuestLog.DefaultStringToState(s);
    }

    protected override void ShowQuests(QuestState questStateMask)
    {
        currentQuestStateMask = questStateMask;

        // If we're viewing active quests, also include preempted ones, which report as unassigned:
        var mask = questStateMask;
        if (mask == QuestState.Active) mask = mask | QuestState.Unassigned;

        noQuestsMessage = GetNoQuestsMessage(questStateMask);
        List<QuestInfo> questList = new List<QuestInfo>();
        if (useGroups)
        {
            var records = QuestLog.GetAllGroupsAndQuests(mask, true);
            foreach (var record in records)
            {
                if (!IsQuestVisible(record.questTitle)) continue;
                // If it's really unassigned and not preempted, skip it:
                if (QuestLog.CurrentQuestState(record.questTitle) == "unassigned") continue;
                questList.Add(GetQuestInfo(record.groupName, record.questTitle));
            }
        }
        else
        {
            string[] titles = QuestLog.GetAllQuests(mask, true, null);
            foreach (var title in titles)
            {
                if (!IsQuestVisible(title)) continue;
                // If it's really unassigned and not preempted, skip it:
                if (QuestLog.CurrentQuestState(title) == "unassigned") continue;
                questList.Add(GetQuestInfo(string.Empty, title));
            }
        }
        quests = questList.ToArray();
        OnQuestListUpdated();
    }
}
I just typed that into this reply. If that doesn't work, let me know and I'll test it out here.

[EDIT: Script updated 2019-09-28.]

To swap out the StandardUIQuestLogWindow script on your quest log window GameObject:

1. From the triple-bar menu in the upper right of the Inspector, select Debug.
2. Inspect your quest log window GameObject.
3. From the Project view, drag the CustomQuestLogWindow script into the Standard UI Quest Log Window's Script field.

This will replace the script while still keeping all of the UI element assignments. Select triple-bar menu > Normal to return the Inspector to its normal view mode.
nivlekius
Posts: 105
Joined: Thu Feb 28, 2019 4:39 pm

Re: Quest states

Post by nivlekius »

Disregard that last one. It was just because your script had preempted to be treated as unassigned not Active. I changed it and it works fine now. Sorry to be a drag. Some day soon, you will have someone else to bug you daily.
Post Reply