Page 2 of 5

Re: Quest states

Posted: Sun Mar 17, 2019 4:09 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 4:11 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 4:17 pm
by nivlekius
nvm it looks like for some reason the console hadn't refreshed. It just ran fine.

Re: Quest states

Posted: Sun Mar 17, 2019 4:52 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 5:03 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 5:07 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 5:49 pm
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"));
            }
        }
    }
}

Re: Quest states

Posted: Sun Mar 17, 2019 6:00 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 6:20 pm
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.

Re: Quest states

Posted: Sun Mar 17, 2019 6:23 pm
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.