The Talos Principle

The Talos Principle

39 ratings
Terminal dialogs
By Grey Heron
A description of how the scripting language for the terminals behaves.
   
Award
Favorite
Favorited
Unfavorite
Introduction
While playing the demo, I was annoyed at the non-backtrackability of the conversation with the Milton Library Assistant (or just Milton for short), since I wanted to see all the content without loads and loads of loading. So as soon as I figured out how to get them out of the .gro file, I tried to read the files defining that conversation. This guide details what I've determined about how they work.

It might be wrong, since I haven't tested my knowledge, only analysed files from the demo. Corrections are, of course, welcome.
Processing model
The terminal dialog system's memory is based on boolean flags. These have global (all terminals in the game see them) and local (only this terminal sees them) scopes.

Two flags are always implicitly set. One is called "InTerminal_<name>", where the part after the underscore is the name of the terminal entity that the script is running on. The other is the name specified in the last goto or next directive to be activated. I'll call that one the state (think "finite-state machine"). Whenever a terminal is activated, the initial state is "Booting".

Apart from goto directives, the engine starts at the top of the file, and proceeds downwards until it gets to the end or an exit directive. A goto causes it to change the state to the name specified in the directive and restart from the top of the file. An exit causes the player to be disengaged from the terminal and the terminal's screen to go blank.
Overall structure
Comments
Comments start with a # and run to the end of the line.

Include directives

include "path"

Process the asset specified by path as if it had been textually included in this one.

Terminal blocks
Display text on the terminal, and define some kinds of player responses.
terminal when (condition) { terminal directive terminal directive terminal directive ... }
condition may be a name in double quotes, in which case it defines the contents of a file, to go with file directives. Otherwise, it is a boolean expression composed from and, or, parentheses, and flags, set flags being true and unset ones being false.

If the name is not matched or the condition is false, the body of the block is ignored.

Player blocks
Define other kinds of player responses, and the journal table of contents.
player when (condition) { terminal directive player directive player directive ... }
condition is a boolean expression. If it is true, the directives add to the list of response buttons displayed at the bottom of the screen, or the journal table of contents.
Terminal directives
These directives can appear in a terminal block.

set
Sets a global flag
set: flagName

clear
Clears a global flag
clear: flagName

setlocal
Sets a local flag
setlocal: flagName
If flagName is in double quotes, then
$(Terminal)
will be replaced by the name of the current terminal.

clearlocal
No idea if this one actually exists or not.

prompt
Sets the prompt text for player input. Like the PROMPT command in cmd.exe, or setting PS1 in bash.
prompt: "string"
For instance,
prompt: ">>>"
will result in the terminal printing three greater-than signs when it starts waiting for the player to click a button.

text
Prints text to the terminal.
text: "string"
text: [[multi-line string]]
The second form can contain newlines, which are reproduced by the terminal. I can't show an example because Steam's BBCode parser gets royally confused by the square brackets. See the "Strings and Text" section for what you can put for the string.

show_text
Shows text in a popup box with a scrollbar, for use in displaying files. Syntax is the same as text

show_image
Shows a texture in a popup box, for displaying images (for instance, the terminal in C★).
show_image: "assetPath"

notext
Don't print anything
notext

enter_code
Asks the user to enter digits and checks them against a variable.
enter_code: variable state_if_correct state_if_wrong

options
Shorthand for a bunch of player blocks.
options:{ "optionText" next: state "optionText" next: state ... }
optionText is the text of the response. next is the state to go to when the button is selected. You can use any of the directives that work in a player block in addition.

goto
Change to a new state and jump to the top of the script.
goto: state

exit
End the terminal session. The player's PoV will move away from the terminal and the terminal's screen will go blank.

slowexit
This is used to go into the ending cutscenes. I'm not sure exactly what it does yet that exit doesn't.
Player directives
These directives can appear in a player block. A block with text and next directives defines a button that will be added to the menu, while an options directive defines a file listing.

text
Defines the text to display on a button. Upon clicking the button, the player character types this in.
text: "string"

short
If this is present, then override the button caption only. The text typed remains as the value of the text directive.
short: "string"

next
Specifies the state to enter (like a goto) when the button is clicked.
next: state

set
Sets a global flag
set: flagName

clear
Clears a global flag
clear: flagName

setlocal
Sets a local flag
setlocal: flagName
If flagName is in double quotes, then
$(Terminal)
will be replaced by the name of the current terminal.

clearlocal
No idea if this one actually exists or not.

options
Defines a file listing.
options:{ header: "headerText" file: "fileName" user: "userName" date: "date" file: "fileName" user: "userName" date: "date" ... }
headerText is the heading that appears in the journal.
fileName, userName, and date are all strings. It is unclear whether the fallback text for fileName or the localised text has to match the terminal block defining the contents of the file. userName seems to refer to the type of content in the base game.

enter_code
Prompt the user for a digit string and test it against a variable.
enter_code: variable matched_state unmatched_state
This directive displays a number pad, with cancel and enter buttons. Cancel acts like exit. Clicking the number buttons types those digits, and clicking Enter causes the digit string to be compared with variable. If it matches, then goto: matched_state, otherwise to unmatched_state.
Strings and Text
Localisation
Strings that begin with "TTRS:" are localised strings. This means they are looked up in the localisation file, "Content/Talos/Locales/<language>/translation_All.txt". The remainder of the localised string is a key/value pair, separated by an equals sign. The key is what is looked up, while the value is, I assume, a default value for if no translation is found.

The localisation file contains similar pairs, one per line. Unlike in the inline strings, newlines are forbidden, and must be represented as "\n".

Formatting
Text printed with the text directive responds to a formatting command. Angle brackets must be escaped as &lt; and &gt; as inin HTML, and presumably other escape sequences, at the very least &amp; work.

A differently coloured text can be achieved by wrapping it thusly:
<span class="strong">help</span> - display this text

Pausing
Output can be slowed by inserting
%wn
into the text at the point to pause, where n is the number of time units to pause. I don't know what the units are, but estimate in the vicinity of 100ms.

Beeping
To create a beep, insert
%sn
into the text at the point of beep. n is an index into the Custom Terminal Text Sounds list in the terminal's properties. So it doesn't have to be a beep at all.

Listings
The file list can be produced by
%p
on a line by itself.

Substitutions
%o"variable"
Replaced with the contents of variable. Used in the elevators to substitute the current access code into a text. variable must be set from the Lua side, because there doesn't seem to be anything setting it in the terminal scripts.

If the variable isn't already set, it gets set to a random three digit integer.

Events
%en
Raises a Lua event named TerminalEvent_n on this terminal. Catch it with a filter like
CustomEvent(terminal, "TerminalEvent_n")
The Journal
(this bit explained by @keks here)

The journal is actually a sort-of terminal, called, appropriately enough, Journal (so "InTerminal_Journal" is set when the script is running in the journal). Its dialog script is set in
World Info Entity (CWorldInfoProperties) World Params Episode Params Journal Terminal Dialog

When the journal is invoked, all the player blocks whose conditions are true collectively form the menu (with the audio logs section appended somehow). If the journal's script doesn't match the flags set by the terminal scripts, things won't show up. Or will show up when they shouldn't.
Lua
Global terminal state is accessible via the CTalosProgress object obtained thusly:
local talosProgress = nexGetTalosProgress(worldInfo)
Noting that worldInfo is automatically set to a suitable entity.

Global flags
These are visible to world scripts, via the methods:
void talosProgress:SetVar(string) void talosProgress:ClearVar(string) number talosProgress:IsVarSet(string)

%o Substitutions
These can be set and retrieved, by
void talosProgress:SetCode("variable", value) number talosProgress:GetCodeValue("variable") number talosProgress:IsCodeSet("variable"
value must be a Number or Boolean.

Events
Terminals raise a number of events:

CustomEvent(terminal, "TerminalEvent_n")
Raised when the %en code is printed.

Event(terminal.FinishedTyping)
Raised when the terminal prints something or the user finishes typing.

Event(terminal.Stopped)
Raised as the terminal goes blank after an exit.

Event(terminal.StoppedWithText)
Raised at some point, I don't know when yet.
8 Comments
Vitani Feb 10 @ 2:30pm 
To play with Milton and read the emails from any terminal use this site. https://mindany2.fr/milton/index.html?lang=enu
Terra Jan 25, 2021 @ 4:37am 
Is there a way to go through all the dialouges, without playing through the puzzles?
LeyendaV Nov 7, 2016 @ 7:33pm 
Very interesting information :taloslol:
zroc Dec 30, 2015 @ 5:18pm 
Hey does the "ctdClearMemory" clear all terminals memory for all the terminals in a level and or the current maps terminals or just for the terminal it was called in?
SMOКER Jan 11, 2015 @ 5:06am 
This guide is very helpful as I am going to sort out some mess of the Chinese localization. Much appreciated.
devnull Dec 16, 2014 @ 11:13pm 
Guess it's time to start modding terminals :3
Rad Dec 14, 2014 @ 5:46am 
Thanks for your effort looking into that.
KJG Dec 14, 2014 @ 4:38am 
This is really interesting, thanks for posting your findings! I love the Milton library assistant, despite the fact only pre-defined choices for responses are avalible to you it really did feel like it was responding dynamically (for me at least). I can't wait to make my own Milton discussion scripts. I've not yet taken the time to have a play with the editor, mainly due to a fear of spoling the game as I'm still on my first playthrough.