Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Announcements, support questions, and discussion for the Dialogue System.
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

Here's what I'd like to do:

Problem 1:

I have a text that reads: "You seem to have X gold coins with you. Did you talk to your associates A, B, C and D about getting a loan?"

Now what I want to do is fill in X, A, B, C and D with respective variables from my Scriptable Object Database. I don't want to use any of the Quest, Item, Location etc. stuff that comes with the system because they don't seem like what I need and I can't seem to find a comfortable way of editing them through code(Maybe I'm missing info on this? help!). My database is of a certain shape and is accessible by code since it's a singleton but I don't know how to access it via the Lua commands.

I just need a method that says: UpdatePartOfText(string remove, string replace).

Problem 2:

I want to present replies depending on my database as well. For example let's say after asking "You seem to have X gold coins with you. Did you talk to your associates A, B, C and D about getting a loan?" I want to put possible answers as the following:

- I talked to A.
- I talked to B.
- I talked to C.
- I talked to D.

So if there was a 5th guy in my database I would add -I talked to E as another option.

Could someone kindly guide me in the right direction?

TL;DR

-How do I update Dialogue Text in a Node of a Conversation of a Dialogue Database (Jesus...)?
-How do I update the aforementioned text with a database of my own making or through variables I save in other instances of classes?\
-Heeeeeeeeeeeeeeeeeeeeeeelp! :oops:
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by Tony Li »

Hi,
tuncturel wrote:Problem 1:

I have a text that reads: "You seem to have X gold coins with you. Did you talk to your associates A, B, C and D about getting a loan?"

Now what I want to do is fill in X, A, B, C and D with respective variables from my Scriptable Object Database. I don't want to use any of the Quest, Item, Location etc. stuff that comes with the system because they don't seem like what I need and I can't seem to find a comfortable way of editing them through code(Maybe I'm missing info on this? help!). My database is of a certain shape and is accessible by code since it's a singleton but I don't know how to access it via the Lua commands.

I just need a method that says: UpdatePartOfText(string remove, string replace).
Here are three ideas:

1. Consider using Dialogue System variables after all. It makes the dialogue writing much easier: "You seem to have [var=gold] gold coins with you. Did you talk to your associates [var=npcA], [var=npcB], [var=npcC] and [var=npcD] about getting a loan?"

Use DialogueLua.SetVariable() to set Dialogue System variables in your code:

Code: Select all

using PixelCrushers.DialogueSystem;
...
DialogueLua.SetVariable("gold", MyScriptableObjectSingleton.GetNumGold()); //(example)     
You can use an OnConversationStart method to set the Dialogue System variables based on the current values in your database.


2. Or, register your C# code with Lua, and use the [lua] markup tag: "You seem to have [lua(GetInt("gold"))] gold coins with you. Did you talk to your associates [lua(GetString("A"))], [lua(GetString("B"))], [lua(GetString("C"))] and [lua(GetString("D"))] about getting a loan?"

Add something like this to your singleton script:

Code: Select all

using PixelCrushers.DialogueSystem;
...
public class MySingletonClass : MonoBehaviour {

    void Start() {
        // If your singleton is a MonoBehaviour, you can register your code here.
        // Otherwise you can add a constructor method.
        Lua.RegisterFunction("GetInt", this, typeof(MySingletonClass).GetMethod("GetInt"));   
        Lua.RegisterFunction("GetString", this, typeof(MySingletonClass).GetMethod("GetString"));   
        Lua.RegisterFunction("GetBool", this, typeof(MySingletonClass).GetMethod("GetBool"));   
    }
} 
This is a totally made-up example since I don't know your singleton code. I just guessed that there might be functions called GetInt(), GetString(), and GetBool().


3. Or, taking a completely different approach, add a script to the Dialogue Manager that has an OnConversationLine method. The Dialogue System calls this method before showing a subtitle. It gives you a chance to modify the text first. For example, say you decide to use angle brackets to specify code to substitute: "You seem to have <X> gold coins with you. Did you talk to your associates <A>, <B>, <C> and <D> about getting a loan?"

Then make the OnConversationLine(Subtitle) method look something like this:

Code: Select all

void OnConversationLine(Subtitle subtitle) {
    subtitle.formattedText.text = FillInVariables(subtitle.formattedText.text);
}

string FillInVariables(string s) {
    // Put your code here to process the string and return the result.
} 
tuncturel wrote:Problem 2:

I want to present replies depending on my database as well. For example let's say after asking "You seem to have X gold coins with you. Did you talk to your associates A, B, C and D about getting a loan?" I want to put possible answers as the following:

- I talked to A.
- I talked to B.
- I talked to C.
- I talked to D.

So if there was a 5th guy in my database I would add -I talked to E as another option.

Could someone kindly guide me in the right direction?
It's easiest if you can add E when you're writing the conversation, and then at runtime make it visible or not based on a condition. In this reply, I'll assume you can do that. (If you need to be able to add new responses at runtime, let me know. Otherwise I'll spare you the wall of text.)

You could once again define variables in the Dialogue Editor such as "talkedToA", "talkedToB", etc. Then set each dialogue entry node like this:
  • Menu Text: I talked to A.
  • Conditions: Variable["talkedToA"] == true
    (You can use the Lua wizard; you don't need to type this manually.)
In the conversation with A, you can set the first dialogue entry node (or whichever node is appropriate) like this:
  • Dialogue Text: Hi, I'm A!
  • Script: Variable["talkedToA"] = true
If you don't want to do that, you could again register your code with Lua and then call that in the Conditions and Script fields:
  • Menu Text: I talked to A.
  • Conditions: GetBool("TalkedToA") == true
    (You can't use the Lua wizard in this case because it doesn't know about your custom-registered functions.)
and:
  • Dialogue Text: Hi, I'm A!
  • Script: SetBool("TalkedToA", true)
I'm sure there are other good ways to approach this, too. So if these don't work for you, let me know.
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

This is some lightning speed response right there Tony, much appreciated. I'm working in the day and it's almost bed time for me so I'll give this stuff a try as soon as I can. I'll reply once I've wrapped my head around your response.
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by Tony Li »

Sounds good!
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

Hi Tony!

I've got to say it again... what an awesome reply you have given to me. I spent some time digesting the text and then I picked the 2nd solution and used the code to register my Lua functions. After a couple minutes I got it working perfectly! Now I can register any function I want and it'll give me the info/variable I need at will. This helps me quite a bit since I don't also have to register existing variables from my Database inside the Dialogu Database one more time.

Now comes the fancy part.

I need to be able add/remove answers depending on the amount of Persons (Person.cs is a class) within a branch. So if a branch has, let's say 5 Persons in there. I want the conversation to go as follows.

- Hi there Tony! It seems you have 3 Persons in your branch. Currently the Persons in your branch are John, Terry and Will. Would you like to hear about what they're up to?
* Yes. Tell me about Johny.
* Yes. Tell me about Terry.
* Yes. Tell me about Will.

Let's say you pick to learn about Terry.

+Yes. Tell me about Terry.
-Terry is a botanist and he's busy picking flowers. What would you like him to do?
* Terry should keep picking flowers.
* Terry should go and treat the weeds in his field.
* Terry should start planting potatoes.

So to explain in a nut shell I want to be able to generate answers on the fly depending first on amount of Persons in a branch. Then I need to be able to generate answers depending on possible things a Botanist can do. Of course I have the respective data again in my Database. I can draw it at will, but I'm not sure how to edit dialogue text and create answers on the fly.

Much appreciate your valuable support. :)
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by Tony Li »

Once again, here are two options:

Option 1: If you know the maximum number of Persons and maximum number of activities that a Person can do, build your conversation with those numbers in mind. For example, make the NPC line:

"Hi there [lua(GetPlayerName())]! It seems you have [lua(GetNumPersons())] Persons in your branch. Currently the Persons in your branch are [lua(GetPersonList())]. Would you like to hear about what they're up to?"

where:
  • GetPlayerName() returns the player's name (e.g., "Tony").
  • GetNumPersons() returns the amount of Persons (e.g., 3).
  • GetPersonList() returns a string listing the Persons (e.g., "John, Terry and Will").
Add a number of responses equal to the maximum number of Persons. For example, if the maximum is 7, add 7 responses. Use the Conditions field to determine whether to show each response. And use the Script field to remember the person that the player chose. For example, response 5 might be:
  • Menu Text: "Yes. Tell me about [lua(GetPersonName(5))]."
  • Conditions: GetNumPersons() <= 5
  • Script: current = 5
(The Script field stores the player's person choice in a Lua variable.)

Link all of these responses to the same NPC line:
  • Dialogue Text: "[lua(GetPersonName(current))] is a [lua(GetPersonJob(current))] and [lua(GetPersonCurrentActivity(current))]. What would you like [lua(GetPersonGender(current))] to do?"
And, similarly to the conditions above, create a number of responses equals to the maximum number of activities that a Person can do. For example, response 3 might be:
  • Menu Text: "[lua(GetPersonName(current))] should [lua(GetPersonActivity(current, 3))]."
  • Conditions: GetNumActivities(current) <= 3
  • Script: DoActivity(current, 3)
where:
  • GetNumActivities(personNumber) returns the number of activities that the specified person can do.
  • GetPersonActivity(personNumber, activityNumber) returns the name of an activity available to a person.
  • DoActivity(personNumber, activityNumber) makes a person do an activity.
This is the easiest of the two options because it uses only the Dialogue System features that you're already familiar with.


Option 2: Construct the conversation on the fly. Here's the overview:

1. Create a new dialogue database.
2. Create a new conversation and add it to the dialogue database.
3. Add the dialogue database to the Dialogue Manager.
4. Run the conversation.
5. Remove the dialogue database from the Dialogue Manager and destroy it.

To create a new dialogue database:

Code: Select all

var database = ScriptableObject.CreateInstance<DialogueDatabase>();
To create a new conversation (pseudocode):

Code: Select all

var conversation = new Conversation();
conversation.id = ???; //assign a unique ID number.
// Add as many dialogue entries as you need. Assign a unique ID to each one.
// Link your dialogue entries by adding Link objects to each dialogue entry's outgoingLinks list.
To add the conversation to the database:

Code: Select all

database.conversations.Add(conversation);
To add the database to the Dialogue Manager:

Code: Select all

DialogueManager.AddDatabase(database);
To start the conversation:

Code: Select all

DialogueManager.StartConversation(...); // Parameters vary.    
To remove the database from the Dialogue Manager and destroy it:

Code: Select all

DialogueManager.RemoveDatabase(database);
Destroy(database); 
To save steps, you could keep the database around and add and remove dynamically-built conversations as needed. But you must remove the database from the Dialogue Manager (i.e., detach it) before manipulating the list of conversations. Then add it back to the Dialogue Manager when you're done creating/modifying conversations.

There are other options, such as making a subclass of UnityUIDialogueUI and overriding the ShowSubtitle and ShowResponses methods. But in the end that's probably messier since you're just manipulating the UI part rather than the underlying data. Unfortunately you can't just write an OnConversationResponseMenu method. In this method you can manipulate the contents of each response, but you can't add or remove responses. The number of responses has to remain the same.
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

Hey Tony,

I just got back from work and read your post. Option 1 seems to be the simpler one indeed. I'm going to weigh the situation and let you know of the outcome.
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

Hi Tony,

I'm implementing the first option already.

I had an issue though and wanted to ask you.

My Dialogue Text reads:
Yes. Tell me about [lua(GetPerson(2))].

And I get this error:
Dialogue System: Lua code 'return GetPerson(2)' threw exception 'failed to convert parameters'

Would you happen to have any headers?
User avatar
Tony Li
Posts: 22104
Joined: Thu Jul 18, 2013 1:27 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by Tony Li »

Hi,

In your C# script, use double for your number parameters. I noticed the formatting of the Data Types Allowed in Lua Functions table was broken. Sorry if this threw you for a loop. I just fixed the manual. Strings and bools work as normal. Numbers are the only unusual case. Lua uses doubles for all numbers. Typecast them in your C# methods. For example:

Code: Select all

string GetPerson(double personNumber) {
    return people[(int)personNumber].name;
}
If you don't want to modify your existing C# method, you can write a tiny wrapper:

Code: Select all

string LuaGetPerson(double personNumber) {
    return GetPerson((int)personNumber); // Cast parameter as int, and call the real GetPerson method.
}
You can still register the function in Lua as "GetPerson" yet make it call the C# LuaGetPerson() method:

Code: Select all

Lua.RegisterFunction("GetPerson", this, typeof(MySingletonClass).GetMethod("LuaGetPerson"));
tuncturel
Posts: 7
Joined: Mon Feb 08, 2016 1:43 pm

Re: Updating Dialogue Text by retrieving Variables from a Scriptable Object Database

Post by tuncturel »

Sounds good. It's a bit of a work around and extra code lying within the method casting the double to int but it works like a charm.

Thanks again for the lightning speed response. I've made considerable headway today! :)
Post Reply