Page 1 of 2

Get the most recent Quest entry and store it as Playmaker variable

Posted: Fri Apr 26, 2024 11:18 am
by Esylin
Hi, I am working on adding a quest entry UI on screen, which will announce the newest quest name.

For example, a quest entry "kill 30 enemies" is activated. Then the UI will pop with the text: "Kill 30 enemies". If the player receive a new quest called "Save Amy", then the UI will replace the old quest text with "Save Amy".

I am using playmaker and it has a "get quest entry" action, which let me store quest entry text as a variable and display the quest entry on screen. but it gets the text of specific quest entry, not the newest quest entry. So it is not possible to track the newest activated quest and store it as a variable like this.

Are there any solution for this? :)

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Fri Apr 26, 2024 12:35 pm
by Tony Li
Hi,

If you are up for a little bit of scripting, you can use the OnQuestEntryStateChange(QuestEntryArgs args) method. Example:

Code: Select all

public TextMeshProUGUI newest; // Assign in inspector.

void OnQuestEntryStateChange(QuestEntryArgs args)
{
    var state = QuestLog.GetQuestEntryState(args.questName, args.entryNumber);
    if (state == QuestState.Active) newest.text = QuestLog.GetQuestEntry(args.questName, args.entryNumber);
}
Note: The other method, OnQuestStateChange(), is called when a quest or a quest entry changes state. In your Playmaker FSM, you'll want to check if the quest has just become active. Or use OnQuestStateChange() like this:

Code: Select all

private List<string> activatedQuests = new List<string>(); // You'll want to reset this when starting a new game or loading a game.

void OnQuestStateChange(string quest)
{
    if (activatedQuests.Contains(quest)) return; // Not interested if it's already been activated.
    if (QuestLog.IsQuestActive(quest))
    {
        activatedQuests.Add(quest);
        newest.text = quest;
    }
}

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Tue Apr 30, 2024 8:35 am
by Esylin
Hmmm....I don't quite fully understand the scripting part but I'll try. Thanks for the scripts!

Another question about the quest system.

The quest system has No Active Quests text if no quests are active.
Quest.png
Quest.png (25.62 KiB) Viewed 927 times
How do I localize this line? Since it is not part of Templates, I can't add localization to it. :|

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Tue Apr 30, 2024 9:59 am
by Tony Li
Hi,

Create a Text Table asset if you haven't already. (Assets > Create > Pixel Crushers > Common > Text > Text Table)

There's a reference manual in Plugins / Pixel Crushers / Common / Documentation if you want to read about text tables, but they should be relatively intuitive to edit.

Add entries for "No Active Quests" and "No Completed Quests", and add your translations for them.

Then assign the Text Table asset to the Dialogue Manager's Display Settings > Localization Settings > Text Table.

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Wed May 01, 2024 7:45 am
by Esylin
Tony Li wrote: Fri Apr 26, 2024 12:35 pm Hi,

If you are up for a little bit of scripting, you can use the OnQuestEntryStateChange(QuestEntryArgs args) method. Example:

Code: Select all

public TextMeshProUGUI newest; // Assign in inspector.

void OnQuestEntryStateChange(QuestEntryArgs args)
{
    var state = QuestLog.GetQuestEntryState(args.questName, args.entryNumber);
    if (state == QuestState.Active) newest.text = QuestLog.GetQuestEntry(args.questName, args.entryNumber);
}

Is this written correctly?
QuestScript.png
QuestScript.png (34.3 KiB) Viewed 914 times
I wrote this and attached it to TMPro as a component. The text does not change. I can't understand how scripts work :(

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Wed May 01, 2024 8:10 am
by Tony Li
Hi,

Make sure it's on the Dialogue Manager GameObject or one of its children. The OnQuestEntryStateChange is only called on the Dialogue Manager hierarchy.

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Wed May 01, 2024 1:07 pm
by Esylin
Tony Li wrote: Wed May 01, 2024 8:10 am Hi,

Make sure it's on the Dialogue Manager GameObject or one of its children. The OnQuestEntryStateChange is only called on the Dialogue Manager hierarchy.
I put the component on Dialogue Manager GameObject now, but it still does not change the text.

Another problem is, my dialogue manager uses Don't Destory on Load, so how can I reference the UI Text from a different prefab in every scene if it only works on Dialogue Manager GameObject?

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Wed May 01, 2024 3:43 pm
by Tony Li
Hi,

Here's an example scene:

DS_ShowNewestQuestEntryExample_2024-04-01.unitypackage

It uses these scripts:

On the Dialogue Manager:

ShowNewestQuestEntry .cs

Code: Select all

using UnityEngine;
using PixelCrushers.DialogueSystem;

public class ShowNewestQuestEntry : MonoBehaviour
{
    // Other scripts can hook into this event to be notified on quest entry changes:
    public System.Action<string> ActivatedQuestEntry;

    // If this script is on the Dialogue Manager, the Dialogue System will
    // call this method when a quest state changes. We need to check this
    // in case the quest was just activated and it has an entry that's
    // already active on start.
    void OnQuestStateChange(string questName)
    {
        if (string.IsNullOrEmpty(questName)) return;
        QuestState state = QuestLog.GetQuestState(questName);
        Debug.Log($"Quest {questName} changed to state {state}");
        if (state == QuestState.Active)
        {
            for (int entryNumber = 1; entryNumber <= QuestLog.GetQuestEntryCount(questName); entryNumber++)
            {
                QuestState entryState = QuestLog.GetQuestEntryState(questName, entryNumber);
                if (entryState == QuestState.Active)
                {
                    string questEntryText = QuestLog.GetQuestEntry(questName, entryNumber);
                    if (string.IsNullOrEmpty(questEntryText) || questEntryText == "nil") continue;
                    ActivatedQuestEntry?.Invoke(questEntryText);
                }
            }            
        }
    }

    // If this script is on the Dialogue Manager, the Dialogue System will
    // call this method when a quest entry state changes.
    void OnQuestEntryStateChange(QuestEntryArgs args)
    {
        QuestState state = QuestLog.GetQuestEntryState(args.questName, args.entryNumber);
        Debug.Log($"Quest {args.questName} entry {args.entryNumber} changed to state {state}");
        if (state == QuestState.Active)
        {
            string questEntryText = QuestLog.GetQuestEntry(args.questName, args.entryNumber);
            ActivatedQuestEntry?.Invoke(questEntryText);
        }
    }
}
On a UI with a Text or TextMeshProUGUI that doesn't have to be in the Dialogue Manager's hierarchy:

NewestQuestEntryUI.cs

Code: Select all

using UnityEngine;
using PixelCrushers;

/// <summary>
/// This script finds the ShowNewestQuestEntry script on the Dialogue Manager
/// and hooks into its ActivatedQuestEntry event to be informed when a quest
/// entry has become active.
/// </summary>
public class NewestQuestEntryUI : MonoBehaviour
{
    public UITextField newEntryText;

    private ShowNewestQuestEntry showNewestQuestEntry;

    private void Start()
    {
        showNewestQuestEntry = FindObjectOfType<ShowNewestQuestEntry>();
        if (showNewestQuestEntry == null)
        {
            Debug.LogWarning("Can't find ShowNewestQuestEntry script.");
        }
        else
        {
            Debug.Log("Hooking into ShowNewestQuestEntry.ActivatedQuestEntry event");
            showNewestQuestEntry.ActivatedQuestEntry += OnActivatedQuestEntry;
        }
    }

    private void OnDestroy()
    {
        if (showNewestQuestEntry != null)
        {
            showNewestQuestEntry.ActivatedQuestEntry -= OnActivatedQuestEntry;
        }
    }

    // This method runs when a quest entry state becomes active.
    // It sets the newEntryText's content to the quest entry text.
    private void OnActivatedQuestEntry(string questEntryText)
    {
        newEntryText.text = questEntryText;
    }
}

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Thu May 02, 2024 12:32 pm
by Esylin
I attached both scripts it kinda worked. The text changed but it only changes during the conversation. As soon as the conversation is over (dialogue UI no longer active) the text quickly become "nil".

Is it possible to store the quest entry string somewhere and keep the entry displayed until the entry is updated again? :)

Re: Get the most recent Quest entry and store it as Playmaker variable

Posted: Thu May 02, 2024 3:33 pm
by Tony Li
Yes. Move your Caption Text where it's not inside your dialogue UI. It must still be in a Canvas, though, since that's a requirement for Text or TextMeshProUGUI components. But if it's in the dialogue UI it will only be visible while the dialogue UI is open (i.e., while a conversation is active).