Page 1 of 1

Dynamically controlling conversations (example)

Posted: Tue Jun 09, 2020 4:12 pm
by callen
Hi, I've been reading the docs as extensively as possible, but I'm stuck on what to do in a basic example of passing data between the player and some NPCs. Let me explain, since it's pretty basic, and please tell me if 1) I'm doing this correctly, 2) what I'm missing?

The player is running around a dance hall filled with NPCs dancing. Each NPC has a dance move, eg "Dance1", "Dance2", ... which they start with. The player can talk to an NPC, and on the first interaction, they will automatically acquire that NPC's dance move. On subsequent interactions, the player can use any of the dance moves the player has acquired thus far, and change the NPC's dance move to it. There may be a couple dozen such dance moves for the player to acquire and spread to NPCs.

I'm wondering a few things:
1. Will I need to make a different actor and conversation for every NPC, or - assuming I'm ok with them all saying essentially the same lines - is there a way to "genericize" a single conversation, and fill-in-the-blanks regarding these dance move names? ("He is currently doing 'Dance 17', should he switch to another move?")
2. How do I store each NPC's starting dance move / current dance move? As a new field on the Actor? Wherever it gets stored, I assume I'll be able to access it in a script, to detect changes and update the visuals to match.
3. How do I check if the player has already "acquired" that dance move, as a condition on the conversation? (I thought about using items, but the wizard for item shows as "<item name> is item" and idk what that means)
4. Should I be making a boolean for each dance move, or are items the correct way to handle this, or something else? (The docs I've found on items so far are a bit sparse)
5. Kind of expanding on 4, is there any kind of "list" structure I should use for these dance moves, instead of unique bool entries for each one?

Thanks for any guidance you can provide. So far things have been very straightforward, the asset is a treat to use!

Re: Dynamically controlling conversations (example)

Posted: Tue Jun 09, 2020 4:45 pm
by Tony Li
Hi,
callen wrote: Tue Jun 09, 2020 4:12 pm1. Will I need to make a different actor and conversation for every NPC, or - assuming I'm ok with them all saying essentially the same lines - is there a way to "genericize" a single conversation, and fill-in-the-blanks regarding these dance move names? ("He is currently doing 'Dance 17', should he switch to another move?")
You can reuse the same conversation.
  • On the Dialogue Editor window's Actors page, define an actor for each NPC.
  • Use the "All Fields" section to add a custom field (e.g., "Custom Move"). You can add it to the actor template on the Templates page so you don't have to manually add it to every actor.
  • Add a Dialogue Actor component to the NPC's GameObject and select the actor from the dropdown.
  • When you start the conversation, specify the NPC's GameObject as the conversation conversant.
    • If the NPC has a Dialogue System Trigger, assign the NPC to Actions > Start Conversation > Conversation Conversant.
    • In dialogue text, you can use the tag [var=Conversant] to show the NPC's name, such as "Hi! I'm [var=Conversant]."
callen wrote: Tue Jun 09, 2020 4:12 pm2. How do I store each NPC's starting dance move / current dance move? As a new field on the Actor? Wherever it gets stored, I assume I'll be able to access it in a script, to detect changes and update the visuals to match.
Yes, as a new field, such as the example "Custom Move" field I mentioned above. In your own scripts, you can use DialogueLua.GetActorField("Some NPC", "Custom Move").asString to get
callen wrote: Tue Jun 09, 2020 4:12 pm3. How do I check if the player has already "acquired" that dance move, as a condition on the conversation? (I thought about using items, but the wizard for item shows as "<item name> is item" and idk what that means)
Although you could maintain the dance moves in the dialogue database somehow, it's probably easiest to record them in a C# script. Add two methods -- one to add a new move to the list, another to check if the player knows a move. Then register those methods as a Dialogue System Lua function, (See Registering Functions.)
callen wrote: Tue Jun 09, 2020 4:12 pm4. Should I be making a boolean for each dance move, or are items the correct way to handle this, or something else? (The docs I've found on items so far are a bit sparse)

5. Kind of expanding on 4, is there any kind of "list" structure I should use for these dance moves, instead of unique bool entries for each one?
In the Dialogue System, the item table isn't used for anything in particular apart from its double-duty as a place to store quest information. If you don't want to use a C# script as mentioned above, you could create an item for each dance move and add a custom field named "Known" or something like that. Or you could create a Boolean variable in the Variables page for each move, such as "MovesKnown.Moonwalk". By putting "MovesKnown." at the front of the variable name, you can easily sort/filter variables that start with "MovesKnown." And when selecting variables in the Conditions and Script dropdowns, periods ( . ) are shown as submenus, so it will group all the move variables under a "MovesKnown" submenu.

Re: Dynamically controlling conversations (example)

Posted: Tue Jun 09, 2020 5:26 pm
by callen
Thanks for the quick reply! Just one thing I'm still unsure about, apart from getting the name with [var=Conversant], how would I include dialogue text for the field "Custom Move", or other info? Is there a way to get arbitrary text from a function with lua somehow? Thanks again

Re: Dynamically controlling conversations (example)

Posted: Tue Jun 09, 2020 8:31 pm
by Tony Li
Hi,

Yup, you can use the [lua(code)] markup tag to include arbitrary Lua expressions in dialogue text. Example:
  • Dialogue Text: "Check out my [lua(Actor["Michael"].Custom_Move)]."
When a conversation starts, the Dialogue System sets a few automatic variables: Actor and Conversant are the display names of the actor and conversant. ActorIndex and ConversantIndex are the Actor[] table indices for the actor and conversant. So if you want to show the dance move of the current conversant:
  • Dialogue Text: "Check out my [lua(Actor[Variable["ConversantIndex"]].Custom_Move)]."
If you want to process the text in a C# method instead, you can add an OnConversationLine method to a script.

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 2:34 pm
by callen
Thanks again! The lua stuff is starting to make sense, but I'm still having a bit of trouble with the 2nd method, using OnConversationLine.

I received the event, with a Subtitle object, and I tried to do custom text replacement on the subtitle.formattedText.text field. Stepping through the debugger I see that the new value of formattedText.text is set, but the string shown in the UI does not reflect this change. This must not be the proper way to do the replacement?
EDIT: Actually never mind, I'm a dummy that DID work just fine, thank you!

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 2:48 pm
by Tony Li
That's the proper way to do it. If you're only setting color, then it's possible that something else (such as a DialogueActor or ActorSubtitleColor component) is overriding your changes. But if you're inserting or replacing text in subtitle.formattedText.text, it should appear.

Note that OnConversationLine is called on any applicable scripts on the Dialogue Manager and both primary participant GameObjects.

Here's an example:

Code: Select all

using UnityEngine;
using PixelCrushers.DialogueSystem;
public class ReplaceDanceMoveField : MonoBehaviour
{
    void OnConversationLine(Subtitle subtitle)
    {
        // Is this my line?
        if (subtitle.speakerInfo.transform == this.transform)
        {
            // Replace '<MOVE>' with the actor's Custom Move field:
            var customMove = DialogueLua.GetActorField(subtitle.speakerInfo.nameInDatabase, "Custom Move").asString;
            subtitle.formattedText.text = subtitle.formattedText.text.Replace("<MOVE>", customMove);
        }
    }
}
If that doesn't help, can you post your OnConversationLine method?

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 4:27 pm
by callen
Thanks for the extra info, and I probably posted my edit just a minute too late to save your time, sorry about that! It definitely works for me as you're saying.

If I could bother you for one last clarification, on the lua side of it again, I'm having trouble understanding how to access dialog database stuff that way. I'm trying to give each dance move an item with a "Known" field, and make the DanceMove field on NPCs one of these items.

So let's say NPC has their DanceMove set to Item "Dance1", which has id 1.

Code: Select all

lua(Actor[Variable["ConversantIndex"]].DanceMove) // resolves to "1", so...
lua(Item[Actor[Variable["ConversantIndex"]].DanceMove]) // resolves to "nil"

I believe this is because lua wants to access it as Item["Dance1"] instead of Item["1"]?

As I dug into it I noticed the same behavior on Actor, where Variable["ConversantIndex"] actually resolves to "NPC" instead of an index, and Variable["Conversant"] is the same "NPC" string.

So I'm really just playing around to develop my skills and workflow. I enjoy the semantic usage of Items for this example, and I could probably register a lua function like "itemIdToName" to fix it. But I want to be sure I'm not fundamentally misunderstanding the relationship between the indexed database and lua environment, if that makes sense.

Thanks for all the guidance!

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 4:44 pm
by Tony Li
Variable["ConversantIndex"] will be the Name field of an actor (e.g., "Bob", or "Bilbo_Baggins").

If the conversant is Bob, and Bob's DanceMove field is "Dance1":

Actor[Variable["ConversantIndex"]] will resolve to the same as Actor["Bob"].

Actor[Variable["ConversantIndex"]].DanceMove will resolve to the same as Actor["Bob"]DanceMove, which is "Dance1".

Are you sure Bob's DanceMove field is really set to a string value such as "Dance1"?

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 5:08 pm
by callen
No I was trying to avoid passing hard-coded strings around to prevent typos, so I set the DanceMove to type "Item" which let me pick "Dance1" in the dropdown. But it's displayed as "Dance1 [1]" so I guess the underlying data is the int 1. Wondering if there's any built-in way with lua to translate back to the string?

Re: Dynamically controlling conversations (example)

Posted: Wed Jun 10, 2020 5:18 pm
by Tony Li
No, the Item, Actor, and Location field types store the internal ID number of the item, actor, or location. You'd have to write a C# method to look it up by ID -- e.g., DialogueManager.masterDatabase.GetItem(id) -- and register it with Lua. But in that case you might as well make a more comprehensive function that gets the entire dance move, or whatever info you need.