Interaction Scripts
Interaction Scripts are Socially Distant script files that describe an interaction between the player and one or more NPCs in the game's chat application. They are used to script branching dialogues within chat rooms, and are one of the main ways the game's narratiive is told. Interaction Scripts, just like mission scripts, are written in sdsh
and can therefore be bundled in Content Mods. See the Modding Fundamentals page for information on how to work with these.
Script structure
Interaction Scripts have a similar structure to mission scripts. They require a metadata
and main_branch
function in order to be loaded by the game. Like a mission script, the metadata
function is used to define attributes about the interaction while the main_branch
function is executed when the interaction starts. The first line of the script must be a unique text-based identifier for the interaction, in the form of a shebang comment (#!chat_interaction_identifier
).
#!chat_interaction_id
metadata() {
type <dm|group|guild>
guild <guild_id>
channel <channel_id>
actor <character_id>
start_type <scripted|auto>
start_message <message_text>
condition <met|unmet> <type> <parameter>
repeatable <true|false>
}
main_branch() {
# Interaction goes here!
}
Note: You must not run any commands outside of a function within an interaction script. Otherwise the script will fail to load during the metadata processing stage.
Metadata
You must define metadata in the script's metadata
function to tell the game how to incorporate the interaction into the rest of the narrative. There are a few mandatory metadata directives, and a few optional ones based on what values you choose for the mandatory ones.
Mandatory metadata directives
type <dm|group|guild>
- Determines whether the interaction takes place in a Direct Message, Group DM, or Guild Channel.actor <character_id>
- Tells the game to include the given NPC, specified by their Narrative ID, in the interaction. DM interactions may only have one actor, while group and guild interactions can have as many as you'd like. Do not specify the player as an actor, since the game does this implicitly. All interactions must have at least one non-player actor, and the first actor specified is considered the interaction's Primary Actor.start_type <auto|scripted>
- Determines how the interaction is started. Auto means that it is started by the player choosing an interaction in the chat UI, while scripted means that the interaction can only be started by another C# orsdsh
script.
Mandatory metadata for start_type auto
interactions
start_message <message>
- This is the prompt displayed in the player's Interact menu for this interaction. If this is not specified, or the provided text is white space, then the interaction will be treated as a scripted interaction.
Mandatory directives for Group and Guild interactions
channel <channel_id>
- For both interaction types, this specifies the Narrative ID of the channel the interaction will take place in. The script will not load if this isn't specified or is specified as white space.guild <guild_id>
- For guild interactions, this specifies which guild the interaction takes place in. The script won't load if this is unspecified or white space. Note that thechannel
directive must also specify a channel that exists within the same guild.
Optional directives
repeatable <true|false>
- determines whether the interaction can be repeated again when it finishes. This is only relevant forstart_type auto
interactions.condition <met|unmet> <type> <parameter>
- Specifies a simple condition that must be met before the interaction can be started. These are ignored for scripted interactions. For info on how to use these, see the Narrative Conditions section on this page.conditions <policy> [parameter]
- Determines the policy for how manycondition
statements must be satisfied in order for the interaction to be available. By default,policy
is set toall
.
Narrative Conditions
Narrative Conditions are used to tell the game when a given interaction can actually be performed by the player. By default, the interaction can only be triggered by the player if all specified conditions are met. Narrative Conditions are specified in the script's metadata
function.
Narrative Conditions take the following syntax, condition <met|unmet> <type> <parameter>
, where:
met
: The condition must be met for the interaction to triggerunmet
: Opposite ofmet
, the condition must NOT be met for the interaction to triggertype
: Specifies the type of the condition that needs to be met.parameter
: Depends on the specified condition type.
Available condition types
The following condition types are available. Descriptions of their expected parameters are also provided. Note that type names are case-insensitive.
Type | Parameter | Description |
---|---|---|
missionCompleted
|
Mission ID | Check that the given mission has been completed. |
interactionCompleted
|
Interaction ID | Check that a given NPC interaction has been completed at least once |
isInMission
|
Mission ID | Checks if the player is playing the specified mission |
friendsWith
|
Character ID | Checks if the player is friends with a given NPC |
blockedBy
|
Character ID | Checks if the player is blocked by a given NPC |
seenProfile
|
Character ID | Checks that the player has encountered a given NPC in the world before |
seenNetwork
|
Network ID | Checks that the player has seen the inside of the given local network before. |
seenDevice
|
Device ID | Checks if the player has hacked into the given device before. |
reachedLevel
|
Integer between 0 and 20
|
Checks that the player has reached the given Skill Level |
worldFlag
|
World Flag | Checks if a given world flag has been set in the current save file |
lifepath
|
Lifepath ID | Checks if the player's chosen Lifepath matches the required one |
missionFailed
|
Mission ID | Checks if the given mission has been failed. This is different from a mission not yet being completed, as a failed mission can't be replayed. |
Condition Policies
The conditions
directive is used to set the policy for how many condition
statements must be met. By default, the policy is all
meaning that all statements must hold true.
The following policy statements are valid:
conditions all
- Default. All conditions must be met.conditions any
- At least one condition must be met.conditions atleast <number>
- At leastnumber
conditions of a uniquetype
must be met.conditions none
- All conditions must not be met.
Actors
Actors are the characters within the interaction. All actors must be specified in the script's metadata, except for the player. The game automatically includes the player in all interactions, with the narrative ID of player
.
The first non-player actor specified in the script metadata is the script's Primary Actor. This is the actor initially possessed by the game when the main_branch
method is first called. For interactions that display in the player's Interact menu, the Primary Actor is the character that gets displayed for this interaction.
You can possess a new actor during the interaction using the actor <actor_id>
command once the interaction has started.
Interaction Commands
Inside the scope of the main_branch
function, you have several Interaction Commands at your disposal to tell a story. Many of these commands will be executed by the script's currently-possessed actor.
actor <actor_id>
Use this to switch the current actor. You may only specify actors defined in the script's metadata
function, or you may specify player
to switch to the player.
say <message>
Send a text message from the current actor. Typing delays and other animations will be handled for you by the game. If the current actor is the player, an animation of the message being typed out will play and the game will block any mouse/keyboard events until it is done.
action <message>
Similar to the say
command, but the message will be italicized as if the character used a /me
command in a real-life chat app.
shrug
Alias for the say
command with the text "¯\_(ツ)_/¯" specified as the message. Yes, we're serious, you can shrug in an interaction script.
mention <character_id>
Generate an @mention
link for a given character, including ones not in the chat. If the given character is the player and the player isn't viewing the conversation, they will hear a noise.
The correct way to use the mention
command is as a command expansion within the say
or action
commands. This will embed the mention within the message's text. Like this:
# Mention the player
say "Hello, $(mention player)"
mission <mission_id>
Send a Mission Attachment message into the chat. You can only do this if the current actor is an NPC. Doing this as the player will halt the interaction with an error.
branch <id> <message>
Define a branch interaction. Branch Interactions allow the player to choose responses within the Interact menu during an NPC interaction.
The first parameter is a unique Branch Identifier, which you can use to check which branch was picked by the player later on. The second argument is the prompt displayed in the Interact menu.
When you define a branch interaction, the current actor will be the character displayed in the interaction menu. This gives the player a visual cue of who they will be responding to. You can also define branch interactions with the player being the current actor, for situations where you just want to let the player say or do something.
branch_picked <id>
Used in tandem wit the branch
command.
This command checks if a given branch interaction was picked by the player, based on its ID. It will return an exit code of 0 if the branch interaction was picked, and 1 otherwise. This allows it to be used within an if
or while
statement inside sdsh
, like so:
actor brodie
branch say_hi "Say hello"
wait_for_choices
if branch_picked say_hi;
then
actor player
say "Hello, $(mention brodie)!"
fi
wait_for_choices
Wait for the player to choose an interaction within the Interact menu. The selected interaction ID is echoed by the command once the player picks their choice. This means you can use this command as a command expansion and store the result in a variable.
You will need to use wait_for_choices
after defining a list of branch interactions with the branch
command, so you can give the player a chance to respond. This is because the branch_picked
command can be used to check more than just the last-picked branch interaction, and thus doesn't wait.
exit
End the interaction.
Tricks
Linking interactions together into a narrative thread
By using Interaction Conditions and smart planning, you can create a narrative thread by chaining multiple interactions together. This appears to the player as a one single interaction, but it is split into multiple steps where you can choose to make any interaction repeatable within a step. The game will also save after each completed interaction in the thread, and the thread can span across multiple channels.
This is especially useful for writing character lore, since you can set up a set of repeatable interactions in a narrative thread that the player can ask an NPC.
The below example shows how to chain 4 separate Interaction Scripts into a 1+2+1 narrative thread.
STEP 1, INTERACTION 1
#!SCRIPT_1
metadata() {
# All scripts in the thread must be "start_type auto"
start_type auto
# The first step can't be repeated.
repeatable false
# For the narrative thread to be cohesive, you may want to share at least one actor across all scripts.
actor brodie
}
STEP 2, INTERACTION 1
#!script2
metadata() {
start_type auto
# "brodie" is our shared actor
actor brodie
# Interactions in Step 2 are repeatable.
repeatable true
# Unless Step 3 has been completed.
condition umet interactionCompleted script4
# Make sure Step 1 has been completed!
condition met interactionCompleted script1
}
STEP 2, INTERACTION 2
#!script3
metadata() {
# Same as the other interaction in step 2
start_type auto
actor brodie
repeatable true
condition unmet interactionCompleted script4
condition met interactionCompleted script1
}
STEP 3, INTERACTION 1
#!script4
metadata() {
start_type auto
actor brodie
# Once this interaction has been completed, the entire thread has been and thus
# can't be replayed. Unless you want it to be, unless both this script and all
# others in the final step and first step must be repeatable.
repeatable false
# Tell the game that we want to allow this interaction if any one of the conditions are met.
# By default, all of them need to be met. In some cases you may want that instead, if you want to
# require all interactions in the previous step to be completed before this one appears.
conditions any
# Require any of the previous step's conditions to be played.
condition met interactionCompleted script2
condition met interactionCompleted script3
}
Locking interactions to within missions
You may want to lock a single interaction, or an entire narrative thread, to only work during a specific mission. This is useful for creating a mission objective that requires the player to talk to someone.
To do this:
- Note the ID of the mission script.
- Make sure all interaction scripts have a
condition met isInMission <MissionID>
condition in their metadata. - If any interactions have
conditions any
in their metadata, change it toconditions atleast 2
. This means that at least 2 unique condition types must be met. - Change the first interaction in the thread to
start_type scripted
. It'll be started by your mission's script with theinteraction <id>
command. - In that same interaction script, add the below code to the start of your
main_branch
to emulate the behaviour of atype auto
interaction. - Note the ID of the final interaction in the thread. Make sure your mission objective is an
interaction
type and that the interaction ID is the same as the ID of the final interaction script in the thread. This makes the objective complete when the narrative thread does.
main_branch() {
branch start "Talk to this NPC"
wait_for_choices
}
This works because, when the game checks for available interactions to show in the Interact menu, it will only show those defined in the current interaction if there's an interaction script running in the channel. The player will therefore be forced to complete the mission interaction.
Locking branches behind skill level
The branch
command supports an optional third parameter that binds it to a skill level condition. If the player hasn't reached that skill level, the interaction will be grayed out.
Introducing a character to the player
By marking an interaction script as start_type scripted
, and starting it from a mission or other interaction script, you can introduce the player to a new character by having them DM the player.
This only works if the interaction introducing the character is type dm
or type group
. This is because DM and Group interactions automatically create their channels when the script is started, and the player will automatically get added to them.
To create an introduction interaction, simply create a scripted interaction with the following metadata, and no branch statements.
#!intro_script
metadata() {
type dm # or group
start_type scripted
actor intro_character
}
main_branch() {
say "$(mention player) Hello!"
say "I am me."
}
This will cause the character to DM the player some messages, and then the interaction will complete. The game will then immediately save after adding the character as a friend to the player. This is because the game creates friendships any time a group or DM interaction completes.
You can then let the player interact with this NPC even further by creating start_type auto
interactions that have a condition met friendsWith <character>
condition.
Failing an interaction
You can fail an interaction by using exit
with a non-zero exit status in main_branch
. If an interaction is failed, no friendships will be created by the game If a future interaction relies on this one being completed, that interaction will no longer be reachable. The game tracks this, meaning failing an interaction can be used to fail a mission objective automatically if an interaction objective requires an unreachable interaction to complete.