Logic & Lua

No scripting is required in the Dialogue System. However, the Dialogue System does offer a general-purpose scripting language called Lua that provides a very powerful method of controlling the flow of conversations, checking and changing quest states, and more. In most cases, you can use simple point-and-click menus.

The Dialogue System uses the data model established by Chat Mapper, another professional dialogue authoring tool commonly used in the industry. Information about all actors, items, locations, variables, and conversations is stored in Lua tables. You can control conversations by specifying Lua conditions and scripts, typically by using point-and-click menus. For those who are interested, the Chat Mapper manual has more background info about Lua and the tables that the Dialogue System uses, in the section titled Scripting with Lua.

Where Lua is Used in the Dialogue System

You can use Lua in the following areas of the Dialogue System:

Point-and-Click Lua

In most places where you can enter Lua code manually (if you prefer), you can also click a '...' button to switch the field into point-and-click mode. In this mode, you can use dropdown menus without having to type any code:

luaWizard1.png

Click '+' to add new conditions or actions. Click Revert to cancel or Apply to apply your selections, which will close the dropdown menus and write the Lua code for you:

luaWizard2.png

How to Write Lua Code

If you prefer to write Lua code directly, you'll find that it's similar to C# or UnityScript. For more information on Lua, see www.lua.org. There are a few syntax differences to be aware of:

Operator C#/UnityScript Lua Lua Example
Not equal != ~= Quest["Kill_5_Rats"].State ~= "success"
Logical AND && and Variable["HasCoke"] and Variable["HasMentos"]
Logical OR || or (Actor["Player"].Age > 21) or Item["Beverage"].IsNonalcoholic
String concatenation + .. Actor["Player"].FullTitle = "Dread Lord " .. Actor["Player"].Name .. " of Skull Island"
if if(x==y) if (x==y) (blank space req'd) if (x == y) then foo() end

Special Exception: LuaInterpreter "and not"

The default Lua implementation that ships with the Dialogue System is LuaInterpreter. This is a fast, lightweight C# implementation of Lua. However, it does not parse one valid Lua syntax:

X and not Y

. You can work around this limit by modifying your syntax slightly or by switching to a different Lua implementation.

Note that Lua, like C# and UnityScript, is case-sensitive.

Checking Values

Here are some examples that you can use in a dialogue entry's Conditions field or the Condition section of a Dialogue System Trigger component.

Check Variable

Check if a Boolean variable has been set true (note the use of double equals "==" to test equality):

Variable["flippedSwitch"] == true

Check Quest State

Check if a quest is in a specific state:

CurrentQuestState("Kill 5 Rats") == "success"

Check Actor and Item Fields

Check if the player is at least 21 and has at least one beer:

(Actor["Player"].Age >= 21) and (Item["Beer"].Count > 0)

Assigning Values

Here are some examples of setting values. You can use them in a dialogue entry's Script field or in the Lua Code action of a Dialogue System Trigger

Set Variable

Set variables (note the use of single equals "=" for assignment):

Variable["spokeToKing"] = true;
Variable["Gold"] = Variable["Gold"] + 50

Set Quest State

Set a quest to a specific state:

SetQuestState("Kill 5 Rats", "active")

Combining Strings

Combining two strings (using Lua's '..' notation):

Actor["Player"].FullTitle = "Dread Lord " .. Actor["Player"].Name .. " of Skull Island"

Special Data & Functions

Special Tables

A table in Lua is similar to an array or dictionary in C# or UnityScript. The following Dialogue Systemspecific tables are available to your Lua scripts:

Table Description Example
Actor[] Two-dimensional table containing all actors. Actor["Player"].Age
Item[] Two-dimensional table containing all items/quests. Item["Anvil"].Weight
Quest[] An alias for the Item[] table.
Location[] Two-dimensional table containing all locations. Location["Starbase"].Description
Conversation[] Two-dimensional table containing all conversations, indexed by ID. Conversation[9].Title
Variable[] One-dimensional table containing user variables. Variable["Alert"] = "Hello!"

In this Lua code:

Actor["Player"].Age = 21

the string "Player" is the key or index. The Actor["Player"] part is the table element. This is sometimes called a record in Lua guides. The Age part is the field. The code above sets the player's Age field to 21.

Special Variables

The Dialogue System also manages these variables in the Variable[] table:

Variable Description
Variable["Alert"] Set to tell the Dialogue System display an alert message.
Variable["Actor"] The name of the actor in the current conversation (if any)
Variable["Conversant"] The name of the conversant in the current conversation (if any)
Variable["ActorIndex"] The actor's index in the Actor[] Lua table
Variable["ConversantIndex"] The conversant's index in the Actor[] Lua table

The variables Variable["Actor"] and Variable["Conversant"] contain the display names of the participants. The variables Variable["ActorIndex"] and Variable["ConversantIndex"] are the indices in the Lua Actor[] table. You can use them in your dialogue entries' Conditions and Script fields. For example, to check if the current conversant is at least 21 years old:

Actor[ Variable["ConversantIndex"] ].Age >= 21

Important Note About Table Indices

Table keys and field names are generically called indices. The Dialogue System follows a convention established by Chat Mapper:

In Chat Mapper tables, replace all space characters and hyphens with underscores in indices.*

Here are some examples:

In the Dialogue Database In Lua
A variable named Kissed the Frog
Variable["Kissed_the_Frog"]
An actor named Mister Big
Actor["Mister_Big"]
The actor's Favorite Color field
Actor["Mister_Big"].Favorite_Color

Special Lua Functions

The Dialogue System adds several useful functions to Lua, including:

Chat Mapper Functions

These Chat Mapper functions are available in Lua to record statuses and relationship values. The descriptions of these functions come from the Chat Mapper documentation:

Status Functions

Chat Mapper can track a status that is defined between any two assets, which is referred to as a mutual status. To set or access a mutual status, use the following two functions:

  • SetStatus( Asset1, Asset2, "status value" )
  • GetStatus( Asset1, Asset2 )

Relationship Functions

Chat Mapper can also track numerical relationship values between any two actors. Relationships can be used to control NPC responses based on a relationship value. These are the relationship fuinctions:

  • SetRelationship( Actor1, Actor2, "relationship type", value )
  • GetRelationship( Actor1, Actor2, "relationship type" )
  • IncRelationship( Actor1, Actor2, "relationship type", incrementAmount )
  • DecRelationship( Actor1, Actor2, "relationship type", decrementAmount )

Unsupported Chat Mapper Functions

The Dialogue System does not support the Chat Mapper functions below, which have have no meaning outside Chat Mapper:

  • TrackVariable
  • TrackStatus
  • TrackRelationship

Other Functions

Alert Functions

To show an alert message:

  • ShowAlert( message )

Quest Functions

To get and set quest states (described in more detail in Quests):

  • CurrentQuestState( questTitle )
  • CurrentQuestEntryState( questTitle, entryNumber )
  • SetQuestState( questTitle, state )
  • SetQuestEntryState( questTitle, entryNumber, state )

While you can technically set quest states by setting the Quest[] Lua table directly, it's better to use these functions because they also update the quest tracker HUD and send OnQuestStateChange messages that your scripts can listen for.

GetLocalizedText() Function

The function GetLocalizedText() retrieves localized versions of fields in the Actor, Item, Quest, or Location tables. For information about setting up localized fields, see Appendix 1: Localization.

  • GetLocalizedText( tableName, elementName, fieldName )

where:

  • tableName is "Actor", "Item", "Quest", or "Location",
  • elementName is the name of an element in the table (e.g., "Player" in the Actor table), and
  • fieldName is the name of the field.

GetLocalizedText() Example:

Say your actors have a "Nickname" field and a Spanish-localized field named "Nickname es". To use the localized version in a conversation, use the [lua(...)] tag to call GetLocalizedText():

  • Dialogue Text: You must talk to [lua( GetLocalizedText("Actor", "Boss", "Nickname") )].

RandomElement() Function

The function RandomElement() returns a random element from a list. It's commonly used in [lua()] tags to add variety to your dialogue text.

  • RandomElement( string )

where string is a string of elements separated by the horizontal bar character ('|').

RandomElement() Example:

Say you have an NPC that greets the character with a random honorific title. In your dialogue database, click on the Variables tab and define a variable named Honorifics that contains this list:

"Esteemed|Revered|Great|Magnificent"

Or, to set it in Lua code:

Variable["Honorifics"] = "Esteemed|Revered|Great|Magnificent"

In the dialogue entry, set the text to:

  • Dialogue Text: Greetings, [lua(RandomElement(Variable["Honorifics"]))] One!

The result is that the NPC will randomly use one of the element in the Honorifics list, such as "Greetings, Esteemed One!" or "Greetings, Great One!"

The source string doesn't have to be set at design time. You can generate it dynamically during gameplay. You can use RandomElement() for other purposes, too, such giving the player a random item upon completion of a quest, or even choosing a random quest out of a list.

Lua Networking Functions

The Dialogue System includes Lua functions that synchronize variables and quest states to all clients using Unity Networking's High Level API (UNET HLAPI). To make these functions available:

  1. Add this Scripting Define Symbol: USE_UNET
  2. Then add a Lua Network Commands component to your player prefab(s). It will add these functions:
  • NetSetBool( variableName, value )
  • NetSetNumber( variableName, value )
  • NetSetString( variableName, value )

The functions above set a variable locally and on all other clients. Use in place of Variable["foo"] = value when you want all clients to receive the value of the variable.

  • NetSetQuestState( questName, state )
  • NetSetQuestEntryState( questName, entryNumber, state )

The functions above set a quest state or quest entry state on all clients. Use in place of SetQuestState() and SetQuestEntryState().

How to Use Lua in Your C# Scripts

DialogueLua Class

The PixelCrushers.DialogueSystem.DialogueLua class is the easiest way to access Lua data.

The PixelCrushers.DialogueSystem.DialogueLua class is the easiest way to access Lua data.

To get and set data in the standard Dialogue System Lua tables – Actor[], Item[], Location[] and Variable[] – use these methods:

Method Description Example
GetVariable() Get the value of a variable bool hasBox = DialogueLua.GetVariable("hasBox").asBool
SetVariable() Set the value of a variable DialogueLua.SetVariable("hasBox", true);
GetActorField() Get the value of a field in an actor int xp = DialogueLua.GetActorField("Player", "XP").asInt;
SetActorField() Set the value of a field in an actor DialogueLua.SetActorField("Player", "Intoxicated", true);
GetItemField() Get the value of a field in an item float weight = DialogueLua.GetItemField("Anvil", "Weight").asFloat;
SetItemField() Set the value of a field in an item DialogueLua.SetItemField("Anvil", "Cost", 50);
GetQuestField() Get the value of a field in a quest. Equivalent to GetItemField() string state = DialogueLua.GetQuestField("Kill 5 Rats", "State").asString;
SetQuestField() Set the value of a field in a quest. Equivalent to SetItemField() DialogueLua.SetQuestField("Kill 5 Rats", "State", "success");
GetLocationField() Get the value of a field in a location string desc = DialogueLua.GetLocationField("Moonbase", "Description").asString;
SetLocationField() Set the value of a field in a location DialogueLua.SetLocationField("Moonbase", "Description", "A desolate lunar spaceport.");

The DialogueLua class automatically converts table indices' spaces and hyphens to underscores. When using the DialogueLua class, you can ignore the note in Important Note About Table Indices. (However, when bypassing the DialogueLua class and using the Lua class directly, you must remember to convert spaces and hyphens to underscores yourself. You can use the DialogueLua.StringToTableIndex() function to do this.)

The DialogueLua.GetXXX() methods return a Lua.Result value. To get a basic data type, use properties such as asString, asInt, asFloat, and asBool. For example:

int age = DialogueLua.GetActorField("Player", "Age").asInt;

The Actor[], Item[], and Location[] tables are two-dimensional, meaning each element in the table has fields. Use the DialogueLua.GetXXXField() methods to access their fields.

The Variable[] table, on the other hand, is one-dimensional. The elements are regular data types (Boolean, string, number, etc.), so you cannot use the DialogueLua.GetXXXField() methods. Instead, use DialogueLua.GetVariable().

bool gotMilk = DialogueLua.GetVariable("Got Milk").asBool;

QuestLog Class

The PixelCrushers.DialogueSystem.QuestLog class provides simpler functions to work with quests. (See Quests.)

Localization

To get the version of a field for the current language, use the localized versions of these methods:

Method Description
GetLocalizedActorField() Get the value of a localized field in an actor (e.g., "Description es" for Spanish)
SetLocalizedActorField() Set the value of a localized field in an actor
GetLocalizedItemField() Get the value of a localized field in an item
SetLocalizedItemField() Set the value of a localized field in an item
GetLocalizedQuestField() Get the value of a localized field in a quest. Equivalent to GetLocalizedItemField()
SetLocalizedQuestField() Set the value of a localized field in a quest. Equivalent to SetLocalizedItemField()
GetLocalizedLocationField() Get the value of a localized field in a location
SetLocalizedLocationField() Set the value of a localized field in a location

For example:

string localizedQuestName = DialogueLua.GetLocalizedQuestField("Kill 5 Rats", "Name").asString;

Lua Class

The PixelCrushers.DialogueSystem.Lua class provides lower-level access to the Lua environment through these functions:

Method Description Example
Run() Run Lua code and return a Lua.Result structure int nextYear = Lua.Run("return Actor['Player'].Age + 1").AsInt;
IsTrue() Run Lua code and return true if the Lua result is true, otherwise false if (Lua.IsTrue("Actor['Player'].Age >= 21")) {...}
RegisterFunction() Register a C# method as a Lua function Lua.RegisterFunction("Exists", null, typeof(MyClass).GetMethod("Exists")); (*see below)

Return Values

Remember that Lua.Run() returns a Lua.Result that can be a Boolean, string, number, or table. To get the Boolean value, you need to add ".AsBool", as in this example:

if (Lua.Run("return Variable['Kissed_the_Frog']").asBool) {
//(Player kissed the frog, so do something.)
}

Similarly, if your variable is a number, you can do this:

int myCoins = Lua.Run("return Variable['numberOfCoins']").AsInt);

Since Lua.IsTrue() always returns a Boolean value (true or false), you don't need to add asXXX to the end:

if (Lua.IsTrue("Variable['Kissed_the_Frog']")) {
//(Player kissed the frog, so do something.)
}

Registering Functions

The RegisterFunction() method lets you tie your own C# code into the Lua environment. This is extremely handy to add new capabilities to your dialogue entry Conditions and Scripts. Use the corresponding UnregisterFunction() method to unregister the C# method from Lua.

For example, say you want a dialogue entry to be available only when a certain GameObject named "White Elephant" is in the scene. You want to be able to set the dialogue entry's Conditions field to this:

  • Conditions: GameObjectExists("White Elephant")

Simply add this class to your scene:

public class MyLuaFunctions : MonoBehaviour {
void OnEnable() {
Lua.RegisterFunction("GameObjectExists", this, typeof(MyLuaFunctions).GetMethod("GameObjectExists"));
}
void OnDisable() {
Lua.UnregisterFunction("GameObjectExists");
}
public bool GameObjectExists(string name) {
return GameObject.Find(name) != null;
}
}

Data Types Allowed in Lua Functions

IMPORTANT: C# methods registered with Lua must use double for numbers, not float or int.

Only use these types in your Lua functions' parameters and return values:

Type Notes
double LuaInterpreter uses doubles for all number types; you can cast to (int), (float), etc inside your function if necessary
bool Use as normal
string Use as normal

Important Notes:

  • Lua treats all numbers as double. In your C# methods, use double instead of float or int.
  • Don't try to register more than one C# method with the same function name. For example, if you write a MonoBehaviour that registers a function, don't add that MonoBehaviour to multiple characters in the scene since each character will try to register the same function. In this case, you may want to add only one instance of the MonoBehaviour to the Dialogue Manager GameObject instead.

Starter Template Script for Custom Lua Functions

You can find a starter template script in Templates ► TemplateCustomLua.cs. To add your own Lua functions, make a copy of this template and customize it as indicated by the comments in the script.

Alternate Format for Type Validation & UWP Compatibility

For Universal Windows Platform compatibility, or to enforce type validation on the function's parameters, use this alternate format to register Lua functions:

using PixelCrushers.DialogueSystem; // Include for SymbolExtensions class.
...
Lua.RegisterFunction("GameObjectExists", this, SymbolExtensions.GetMethodInfo(() => GameObjectExists(string.Empty)));

Lua Observers

In some cases, you may want to be notified when the value of a Lua variable changes.

The DialogueManager class lets you manage observers on Lua expressions using these methods:

Method Description Example
DialogueManager.AddLuaObserver() Add an observer on a Lua expression DialogueManager.AddLuaObserver("Variable['Credits']", LuaWatchFrequency.EveryDialogueEntry, OnCreditsChanged);
DialogueManager.RemoveLuaObserver() Remove an observer DialogueManager.RemoveLuaObserver("Variable['Credits']");
DialogueManager.RemoveAllLuaObservers() Remove all observers DialogueManager.RemoveAllLuaObservers();

AddLuaObserver() adds an observer on a Lua expression that will be checked on a specified frequency. The frequency can be EveryUpdate, EveryDialogueEntry, or EndOfConversation. If the expression changes, the Dialogue System will invoke a delegate that takes the form:

void MyDelegate(LuaWatchItem luaWatchItem, Lua.Result newValue) {...}

Example:

DialogueManager.AddLuaObserver("Variable['Credits']", LuaWatchFrequency.EveryDialogueEntry, OnCreditsChanged);
void OnCreditsChanged(LuaWatchItem luaWatchItem, Lua.Result newValue) {
Debug.Log("Number of credits changed to: " + newValue.AsInt);
}

Note: For best performance, limit the number of observers you set, especially when the frequency is EveryUpdate. Each observer requires an extra Lua call to evaluate the current state of the Lua expression.

RemoveLuaObserver() removes an observer. The observer is identified by the Lua expression, which should exactly match the same string that you passed to AddLuaObserver(). RemoveAllLuaObservers() removes all observers.

Lua Console

The Lua Console component adds an in-game console in which you can run Lua expressions. By default, the console is mapped to ~+L but you can change the key binding.

Lua Under the Hood

LuaInterpreter

The Dialogue System uses Liu Junfeng's Lua Interpreter under MIT License. The included version has been modified to be compatible with UWP projects. It does not implement the following functions:

  • basic library: collectgarbage(), load(), rawequal(), rawlen(), rawset(), xpcall()
  • coroutines library: all functions
  • modules library: all functions
  • string library: dump(), format(), gmatch, gsub, match
  • table library: pack(), unpack()
  • bit32 library: all functions
  • input library: functions defined but disabled (to prevent malicious code insertion)
  • os: functions defined but disabled (to prevent malicious code insertion)
  • debug: all functions

Using Other Lua Implementations

The Dialogue System provides a PixelCrushers.DialogueSystem.Lua wrapper class which isolates the actual Lua implementation from the rest of the Dialogue System. If you want to use a different Lua implementation, you only need to replace these classes:

  • Lua.cs: The main wrapper class.
  • LuaTableWrapper.cs: A wrapper that allows you to handle Lua tables similarly to C# dictionaries.
  • DialogueLua.cs: Contains some Lua Interpreter-specific optimizations.

<< Welcome to the Dialogue System for Unity! | Cutscene Sequences >>