Best way to keep track of played Conversations?

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
2linescrossed
Posts: 47
Joined: Tue May 16, 2023 10:37 pm

Best way to keep track of played Conversations?

Post by 2linescrossed »

Hi! There's two things I want to create in particular, but I'll describe the first one first.

I'm currently building a game that has two primary means of displaying NPC conversations - through randomly playing conversations at a table, and conversations elsewhere which are reminiscent of the Fire Emblem 'Supports', being like miniature cutscenes that show two characters interacting.

I want to be able to save which conversations that the player has seen, and in the latter case, create a menu where players can re-view 'supports' that have played out. In the case of the supports involving player characters - I would also like to save choices made by the player so that re-viewing the cutscene does not let them change their decisions.

I imagine that the best way to handle this is through the usage of the Dialog Database's own variables, but I would like to know what the most efficient way of doing this would be. My previous solution is primitive and unrefined - being a second list keeping track of which conversations have been played.

In the case of the prior, I would also like to make it so that if a conversation is marked as 'seen', then it is less likely to appear again. I imagine I could do this easily if I mark the conversation with a variable, but I'm unsure where to put that variable specifically. Currently, the way I handle conversations is with the manager having a list of every conversation/Dialog in the game as scriptable objects, before narrowing it down to characters currently present in the scene.

Thanks in advance for suggestions.
User avatar
Tony Li
Posts: 22112
Joined: Thu Jul 18, 2013 1:27 pm

Re: Best way to keep track of played Conversations?

Post by Tony Li »

Hi,

This custom functionality will require some scripting, but not too much script.

The approach that requires the least effort (at the expense of slightly larger saved game files) is to:

1. Tick the Dialogue Manager GameObject's Other Settings > Include SimStatus.

2. To check if a conversation has been played, check DialogueLua.GetSimStatus(conversationID, 0), such as:

Code: Select all

public bool HasConversationPlayed(string conversationTitle)
{
    var conversation = DialogueManager.masterDatabase.GetConversation(conversationTitle);
    if (conversation == null) return false; // (Invalid conversation title.)
    return DialogueLua.GetSimStatus(conversation.id, 0) == DialogueLua.WasDisplayed;
}

To give priority to conversations that haven't been played before, you could do something like this:

Code: Select all

public string ChooseConversation()
{
    var unplayed = new List<string>();
    var played = new List<string>();
    foreach (Conversation conversation in DialogueManager.masterDatabase.conversations)
    {
        if (conversation.Title.StartsWith("Support/")) continue; // (Ignore support conversations.)
        if (HasConversationPlayed(conversation.Title))
        {
            played.Add(conversation.Title);
        }
        else
        {
            unplayed.Add(conversation.Title);
        }
    }
    // Return a random unplayed conversation. If no unplayed, return a random already-played conversation.
    if (unplayed.Count > 0) return unplayed[Random.Range(0, unplayed.Count)];
    if (played.Count > 0) return played[Random.Range(0, played.Count)];
    return string.Empty; // (No available conversations.)
}

It's possible to track support conversations to replay them, but it's a little more work. Make a subclass of StandardUISubtitlePanel and override OnClick() [called when the player clicks a response] and ShowResponses(). Something like:

Code: Select all

// Dictionary keyed by conversation title.
// Each value is a conversation's dictionary where key is
// preceding subtitle entry's id and value is response entry's id.
private Dictionary<string, Dictionary<int, int>> choices = new Dictionary<string, Dictionary<int, int>>();

private Dictionary<int, int> GetCurrentConversationChoices()
{
    // Get current conversation's dictionary of conversation choices:
    Dictionary<int, int> conversationChoices;
    if (!choices.TryGetValue(DialogueManager.lastConversationStarted, out conversationChoices))
    {
        conversationChoices = new Dictionary<int, int>();
        choices[DialogueManager.lastConversationStarted] = conversationChoices;
    }
    return conversationChoices;
}

public override void OnClick(object data)
{
    var conversationChoices = GetCurrentConversationChoices();

    // Record response ID:
    var precedingID = DialogueManager.currentConversationState.subtitle.dialogueEntry.id;
    var responseID = (data as Response).destinationEntry.id;
    conversationChoices[precedingID] = responseID;

    base.OnClick(data);
}

public override void ShowResponses(Subtitle subtitle, Response[] responses, float timeout)
{
    // If we've recorded a response, "click" it instead of showing response menu:
    var conversationChoices = GetCurrentConversationChoices();
    var precedingID = DialogueManager.currentConversationState.subtitle.dialogueEntry.id;
    int responseID;
    if (conversationChoices.TryGetValue(precedingID, out responseID))
    {
        foreach (Response response in responses)
        {
            if (response.destinationEntry.id == responseID)
            {
                OnClick(response);
                return;
            }
        }
    }
    base.ShowResponses(subtitle, responses, timeout);
}
Post Reply