What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Announcements, support questions, and discussion for the Dialogue System.
AoF
Posts: 241
Joined: Sun May 12, 2019 8:36 pm

What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by AoF »

My game has a concept of days represented in a variable called Day. I'd like to make a conditional that says, "if this other node was displayed yesterday or before." How would I do that? I know I could make a variable and record the day the other node was recorded, but is there a worthwhile way of making this work automatically?

It would be convenient if I could have a field on every dialogue entry called, "lastDayDisplayed". If that existed, I could just check if "otherNode.lastDayDisplayed < Day". I think the way I would set this field is by implementing a OnConversationLine that assigns it? Then in my condition, I think I could do something similar to a simstatus like this: "Dialog[42].lastDayDisplayed", but I'm not sure if you can do that on fields.

Should I avoid doing this because it will use up a lot of disk space, or is that not worth thinking about?
User avatar
Tony Li
Posts: 21721
Joined: Thu Jul 18, 2013 1:27 pm

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by Tony Li »

(Edit: I didn't like using entry IDs for this. I rewrote it to use the entry's Title.)

The save system doesn't normally save dialogue entry fields. I'd add them as variables instead so the info will be included in saved games. And perhaps only for dialogue entries that are specially marked to keep track of the day. For example:

1. Define a custom dialogue entry field named "rememberDay" (Boolean). Set it true for any dialogue entry that should remember the last day it was visited.

2. Add an OnConversationLine method like this:

Code: Select all

void OnConversationLine(Subtitle subtitle)
{ 
    var entry = subtitle.dialogueEntry;
    if (Field.LookupBool(entry.fields, "rememberDay"))
    { // Record that this entry was visited today:
        var today = DialogueLua.GetVariable("Day").asInt;
        DialogueLua.SetVariable("day" + entry.Title, today);
    }
}
3. Register a Lua function DayHasPassed(#):

Code: Select all

Lua.RegisterFunction("DayHasPassed", this, SymbolExtensions.GetMethodInfo(() => DayHasPassed(string.Empty)));
...
bool DayHasPassed(string title)
{
    var entry = FindEntryByTitle(title);
    if (entry == null) return false;
    var today = DialogueLua.GetVariable("Day").asInt;
    var entryDay = DialogueLua.GetVariable("day" + entry.Title).asInt;
    return entryDay < today;
}

DialogueEntry FindEntryByTitle(string title)
{
    foreach (var conversation in DialogueManager.masterDatabase.conversations)
    {
        foreach (var entry in conversation.dialogueEntries)
        {
            if (entry.Title == title) return entry;
        }
    }
    return null;
}
In your conditions, check DayHasPassed("Some Title") where "Some Title" is the Title of the entry that you want to check. You could use entry IDs, but I think Titles are more readable.
User avatar
Rallix
Posts: 15
Joined: Mon Jul 08, 2019 9:02 am

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by Rallix »

Hi Tony,
speaking of best practices, would you say a variant of this approach would also be the best choice for the following:

1) Characters dropping into the conversation if they're nearby.
Similarly to how Infinity Engine games did it – during a dialogue, if a character was standing close to the conversants, they'd comment. Currently, I check the presence of all possible participants when the conversation starts and use variables like Alice.present and Bob.present.
I just wondered if there's a better/simpler/more unified way and making a function like IfPresent(character, distance=7m) would seem to do the trick.

2) Exhausting all (some) dialogue options before the final one.
Currently, I think I'm creating too many one-time boolean variables and it's hard to stay organised as the global variable table keeps expanding fast. I'm essentially manually entering and setting option1Exhausted, option2Exhausted etc. and check option1Exhausted && option2Exhausted && … on the final one.

I know this has most likely been asked a million times already, but since there are many ways to do it, I'm curious what would you consider the most scalable approach, because mine currently feels a bit overcomplicated. ;)
User avatar
Tony Li
Posts: 21721
Joined: Thu Jul 18, 2013 1:27 pm

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by Tony Li »

Hi,
Rallix wrote: Mon Jul 08, 2019 1:49 pm1) Characters dropping into the conversation if they're nearby.
Similarly to how Infinity Engine games did it – during a dialogue, if a character was standing close to the conversants, they'd comment. Currently, I check the presence of all possible participants when the conversation starts and use variables like Alice.present and Bob.present.
I just wondered if there's a better/simpler/more unified way and making a function like IfPresent(character, distance=7m) would seem to do the trick.
Adding an IsPresent() custom Lua function is a good way to go. Then you can set up side-comment nodes like this:
  • Link Priority: AboveNormal
  • Conditions: IsPresent("Alistair", 7)
  • Dialogue Text: "I'm saying some quippy side comment."
(Setting the link priority to AboveNormal gives preference to Alistair saying this comment over any other characters saying something -- assuming the Conditions are true.)
Rallix wrote: Mon Jul 08, 2019 1:49 pm2) Exhausting all (some) dialogue options before the final one.
Currently, I think I'm creating too many one-time boolean variables and it's hard to stay organised as the global variable table keeps expanding fast. I'm essentially manually entering and setting option1Exhausted, option2Exhausted etc. and check option1Exhausted && option2Exhausted && … on the final one.
I'm not sure exactly what you're using it for, but what about using SimStatus?

Also, for managing global variables, many devs will add grouping names to the beginning of their variable names, such as:
  • Battle.FirstBlood
  • Battle.NumTrollsKilled
  • Wardens.Joined
  • Wardens.Offended
  • Wardens.MetFounder
It makes it easier to sort and filter for a specific category of variables such as "Wardens".
User avatar
Rallix
Posts: 15
Joined: Mon Jul 08, 2019 9:02 am

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by Rallix »

Thank you very much! :)
Tony Li wrote: Mon Jul 08, 2019 2:22 pm (Setting the link priority to AboveNormal gives preference to Alistair saying this comment over any other characters saying something -- assuming the Conditions are true.)
Thanks for this tip.
Tony Li wrote: Mon Jul 08, 2019 2:22 pm I'm not sure exactly what you're using it for, but what about using SimStatus?
That's exactly what I need.
I'm using it for something like here; i.e. "ask three background questions before the final, most important question appears". Just to make sure the player character has all information before moving on. Or to block something with a sequence I would not like to trigger again.
I'm using articy:draft to write the dialogue, so I'll probably add essentially the same thing as SimStatus as a property to certain important dialogue nodes and use getProp / setProp, seeing that it's supported by the Dialogue System.
Default to Untouched, WasOffered when passing through an "important Hub" and WasDisplayed as soon as an option is picked.
Tony Li wrote: Mon Jul 08, 2019 2:22 pmAlso, for managing global variables, many devs will add grouping names to the beginning of their variable names, such as:
  • Battle.FirstBlood
  • Battle.NumTrollsKilled
  • Wardens.Joined
  • Wardens.Offended
  • Wardens.MetFounder
It makes it easier to sort and filter for a specific category of variables such as "Wardens".
Yup, I'm doing this ("CharacterLocation.DialogueXXX"), although Articy only supports one level of grouping, so I'm relying on alphabetical sorting for the rest; although that's not related to the Dialogue System.

Again, thank you for the advice!
AoF
Posts: 241
Joined: Sun May 12, 2019 8:36 pm

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by AoF »

Tony Li wrote: Mon Jul 08, 2019 12:39 am

Code: Select all

DialogueEntry FindEntryByTitle(string title)
{
    foreach (var conversation in DialogueManager.masterDatabase.conversations)
    {
        foreach (var entry in conversation.dialogueEntries)
        {
            if (entry.Title == title) return entry;
        }
    }
    return null;
}
Normally, I don't care about performance (unless I know that a human can notice it), but I'm wondering if I should be worried about this code. It's looking at every single conversation then every single node. Is this probably fine, even in large databases?

Since I don't think these things change at runtime, I could easily run it once, cache a Dictionary<string, DialogueEntry> (where the key is the entry title), and get O(1) lookups. I just don't know if it's worth optimizing. I also don't know if naively caching like that will work correctly.
AoF
Posts: 241
Joined: Sun May 12, 2019 8:36 pm

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by AoF »

I tried out this code, and the problem is that when you haven't visited the node, its day value defaults to 0. Is there a way to make it default to a really big number? In this case, this happens to be a response choice that hasn't been picked yet. I'm not sure if that's relevant.
User avatar
Tony Li
Posts: 21721
Joined: Thu Jul 18, 2013 1:27 pm

Re: What's the best way to make a conditional that says, "if a day has passed since you visited this other node"?

Post by Tony Li »

Try this:

Code: Select all

var entryDay = DialogueLua.GetVariable("day" + entry.Title).asInt;
if (entryDay == 0) entryDay = System.Int32.MaxValue; //<--ADD THIS.
Post Reply