Convo system


The Convo system is a re-working of the TalkDialogue system. The Convo system uses SNTX to indicate topics and choices through the use of SNTX nodes.

The Convo system uses TGF as an intermediate file format which is then translated into pure SNTX files. A Python script called TGF2Convo is this converter.

The system is divided into 2 parts:

  1. Convo database. This is the content of dialogue and describes the Dialogue Tree (DT).
  2. Convo functions. This is the game-side application of the Convo db.

Convo databases

A Convo database is on a per-NPC basis. It contains every dialogue that an NPC may have in the game.

It is formatted as a Markdown file (SNTX) to better see the flow using Markdown styling, but only particular lines are filtered as actual data.

Convo databases are parsed into Dictionaries (key-value pairs). The key is a lookup and has a particular syntax.

Convo key

The basic tokens are:

  • == Topic
  • ++ Choice
  • -> Entry point
  • ? refers to a Condition statement; this is used as prefix to a possible CSV of different Conditions
  • $ refers to an Accomps Condition
  • @ refers to a State Condition (a State condition refers to a state value of the NPC itself)
  • - refers the variant of a particular Choice or Topic
  • ~ refers to the directive or function of Choice or Topic.
  • , multi-purpose; in the context of values, it forms a CSV list; in the context of Conditions, it concats as AND or &
  • | concats Conditions as OR or |

These tokens are arranged in a particular way to mean something for the Convo system to look up.

Entry key

The first and most basic key is the topical entry key.

['->'] = intro

This means, that without specified conditions, intro is the first topic that this NPC will talk about. A condition, whether an Accomps condition or a State condition, can be specified.

['-> ?@dead'] = dead_intro

['-> ?$talked_to_chuck'] = talked_again_intro

Equality testing can be used. If equality testing is not used, then the condition is tested for existence and the value of 1.

['-> ?@dead=0'] = intro

Conditions can be tested against their absence, too:

['-> ?@!dead'] = intro

Multiple conditions

Multiple conditions can be used, but must be concatenated by , or | and must not contain any whitespace as whitespace is the basis for the whole key’s parsing.

['-> ?@ok,$talked_to_chuck'] = intro
['-> ?@ok|$talked_to_chuck'] = intro

Lastly, a condition can be reversed by the ! symbol. If an equality test is made, then the equality test is reversed.

['-> ?$!talked_to_chuck'] = did_not_talk_to_chuck_intro

Topic key

A Topic is the meat of NPC dialogue. A Topic is defined by the == symbol.

==<topic_name> -<variant> @<condition> ~<directive>

Each token is prefixed by a symbol that signifies that that data is for.

Variant parameter and vars

The variant parameter is made to allow randomness in picking.  It begins at Topics, where you can declare how many variations there are for any particular topic.

['==intro ~vars'] = 2

That signifies that the intro Topic has 2 variations. These variations can thus be accessed:

['==intro -1 ~text'] = 'What do you want?'
['==intro -2 ~text'] = 'Are you even allowed here?'

The above defines the text of the intro for both variations that were declared.

Declaring ~vars is important as the random number is generated using this value, and the keys are searched for with this parameter value. If ~vars is not specified, then the default value is 1.

Choices of Topics choices directive

A Topic usually has a list of Choices to choose from. These Choices are defined under the Topic itself. If there is a variant, then the variant should be specified. (N.B. If the system fails to find a topic variant it will always revert to the -1 variant, so it is important to always use -1 as the default variant name. It is also possible to omit the use of any declared variants when convenient so long as the -1 exists.)

['==intro -1 ~choices'] = /group1
['==intro -2 ~choices'] = allowed

(N.B. the use of /group1 as a choice group, which will be explained below, as well as the concept of Choices.)

However, if you omit the variant:

['==intro ~choices'] = /group1

Then it is assumed that regardless of Topic variant, these Choices will be used.

Choice key

A Choice is prefixed by ++. It is defined in the same way as Topics using the same syntax, but the process of creating and using a Choice is obviously different.

Declaring a Choice

The first step is to declare the existence of Choices.

['++your_name ~text'] = Name
['++your_job ~text'] = Job
['++allowed ~text'] = Allow
['++ecells ~text'] = E-cells
['++contraband ~text'] = Contraband
['++robots ~text'] = Robots
['++memories ~text'] = Memories

The above shows how a Choice key (eg contraband is given a ~text parameter value "Contraband" which is the string that will be displayed.

The Convo system, like the TalkDialogue system, assumes that the name of the selected Choice is the same Topic name that will be selected. Thus, there is no need to provide another directive (e.g. ~topic) to resolve the Topic name.

Choice Groups

Groups can be created for Choices in order to retrieve them more conveniently especially when authoring dialogue. A Choice is considered a Choice Group if the / is prefixing the Choice name.

This is how we declare Choice Groups.

['/group1'] = your_name,your_job
['/group2'] = allowed
['/group3'] = ecells,contraband,robots,memories
['++/group4'] = rules

Here the syntax is compact because the lookup is very direct. This is an example for a call to a group:

['==intro -1 ~choices'] = /group1

When the system processes that key and then sees /group, it immediately looks the key up; that’s what I mean by direct. Then if/when the key is found, the list of choices is expanded.

Initial key collection

The initial key collection happens when a Convo is initiated.

The way the keys are searched is no longer in a fixed format like TalkDialogue. Instead the keys’ strings are parsed and tokens are checked for existence and values.

If the system requires the Topic text for intro with variant -2 each key accessed and checked against the best match. By ‘best match’ we also consider NPC State and Accomps in finding the best match.

Our matching usually begins when we know the Topic we are entering. We first search for variant (i.e. vars) of the Topic to determine if we need to find a random number. From the first thing we want to do is display the Topic text. To do that:

  • Each key is searched for the Topic name intro, which resolves to ==intro.
  • It also looks for the ~text directive since the text is what is required.
  • Look for the generated variant number is found, e.g. -2
  • If all of the above 3 are found, this key’s value is used.
  • However, as mentioned above, the default variant -1 does exist, so that is also collected on the chance that -2 doesn’t exist. If -2 does appear, then the resulting value is replaced by the one that matches it better.

Let’s say that the collected keys are this.

['==intro -2 ~text'] = "What do you want?"
['==intro -2 ?@ok,$talked_to_chuck ~text'] = "You've talked to me before."
['==intro -2 ?@dead|$talked_to_chuck ~text'] = "I'm either dead or you've talked to me before."

N.B. The abovementioned process is the preliminary — not complete — Initial Key Collection process, and now we must consider Conditions, described below.

State and Accomps filtering conditions in keys

Let’s add Conditions to the process described above. We can consider this as a next step after the initial key collection, which ignored Conditions. This 2nd step, can be called Filtering Keys for Conditions.

Assuming the collected keys written above:

  • The first thing is to recognise the default ~text value. When the keys are being processed, when it comes upon a ~text directive with no Condition it puts in a special default condition variable.
  • In each of these keys, we look for the token that starts with ? which signifies that it is a Condition being defined.
  • Then we parse the Conditions and do a check. We consider the , and the | operators, if the Condition is True in their respective comparison (i.e. Accomps vs State) we take note that this key is the one to use.
  • However, if there other keys down being processed that also results in True, then those key values will replace the older ones. Thus, the priority is the one that gets processed last.
  • However, if any of the Conditions could not be resolve as True, then the default condition variable is used.

Displaying the Choices

Now that the Topic text has been selected, we need to display the Choices. Although we have split the documentation section between Topics and Choices, the process of collecting their keys happen in the same loop. We have to view it as though we were collecting keys in the same stage as we were collecting ~text keys.

Now, however, we collect ~choices.

  • As each key is being searched for for the Topic name ==intro, we look for the ~choices directive.
  • Look for the default variant number -1 and the generated variant number, e.g. -2

Let’s say we’ve collected the following:

['==intro -1 ~choices'] = /group1
['==intro -2 ?@ok,$talked_to_chuck ~choices'] = /group2
['==intro -1 ?@dead|$talked_to_chuck ~choices'] = /group3

Like before, we Filter for Conditions.  Once we determine which key fits the Conditions best, we use the value.

Then we process the Choices. We process it as a Choice Group if the tokenized CSV value starts with /, whereby we take the group’s name and prepend it with ++, which results in something like ++/group which is the key to use to lookup the actual Choices that will comprise this Topic.

Once the Choices are selected, then another lookup is made in order to determine their text value. That is simply done by using the ~text directive:++your_name ~text which is the key used to query the db.

Conditions in Entry Topic vs Topic

The Entry Topic is special because it contextualises everything at the beginning. Thus, the Entry Topic can vary depending on Conditions. Contrast this with Topic Conditions, which only control the display of Topic text or Choices. Entry Topic Conditions control which Topic is actually selected.

DoAccomps and DoState directives

DoAccomps sets or adds keys to the Accomps, while DoState modifies the State of the NPC or entity in question.

Both of these ‘Do’ functions can be used in Topics or Choices. If used in a Topic, it run when the Topic is displayed. If used in Choices, then it will run the moment the player selects the Choice.

Convo functions

All Convo functions must be passed the NPCObject because State Conditions needs to be checked.

ConvoEntry (NPCObject)

ConvoEntry is the point where the Convo starts. It needs a reference to the NPC (eg NPCObject) that is being targeted.  The NPCObject should contain information about the NPC’s state (in order to process State conditions). Convo has implied access to the accomps.

ConvoEntry tries to locate the entry topic though the –> key.  What the above key says is: the topical entry point the default condition.

If –> @dead this means the topical entry point if the NPC’s attribute is ‘dead=1’. Note that = may be supplied but is not required. If not supplied, then =1 is assumed

If –> $talked_to_chuck means the topical entry point if accomps has ‘talked_to_chuck’ key. Like the above, =1 is assumed if a value to compare is not supplied.

When the entry point is determined, then system calls ConvoDisplay.

ConvoDisplay (NPCObject)

This function is responsible for displaying the topic and the choices for that topic.


%d bloggers like this: