Skip to content
Adam Sechrest edited this page Dec 15, 2022 · 23 revisions

(Work in Progress)

Introduction

The concept of triggers is that Lich will respond automatically to certain game output without the need to run a separate script. Historically, the ability to set triggers is a function of the Front End being used. Lich-based triggers are front-end agnostic and built within Lich itself. Thus, they can be used across and between front ends. Simple triggers have a fairly low barrier to entry for users, and more advanced uses can allow some complex handling without writing a dedicated script. Further, triggers can supplement the use of curated or custom Lich scripts. At some point, though, the complexity of an action calls for writing a dedicated script.

Underlying Code Structure

Lich's triggers are defined by the user in a yaml file along with the desired trigger responses. Triggers are then monitored for and acted upon with a persistent companion script called trigger-watcher. Fundamentally, Lich's triggers use class Flags which is defined in the events script and at a basic level monitor for user-defined game output. In order for Lich triggers to work, triggers must be defined properly in the yaml, and both the events and trigger-watcher script must stay running.

Structure of a Trigger

The structure of each trigger is as follows:

  • The top level heading of lich_triggers (required)
  • Beneath that and indented, the name of this specific trigger. This is arbitrary -- name it what you want.
  • Beneath that and indented, a section called triggers:. This represents the game output you want the trigger to look for and then take action.
  • Beneath that and indented, a section called responses:. When Lich sees the output you defined in the triggers section, it will process these trigger responses.
  • Optionally, a section called use_with_scripts: that specifies that the trigger should only fire when one or more of those scripts are running.

Let's look at an extremely simple trigger.

# LICH TRIGGERS
# Yaml for Lich-based triggers

lich_triggers:
  wave_hello:
    triggers:
      - Bobafett just arrived
    responses:
      - wave Bobafett

This trigger recognizes when a character named Bobafett enters the room, and it waves to them. What's happening behind the scenes is that Lich is watching all game output lines for Bobafett just arrived. When it sees that, it takes the action you've defined in responses and it will send wave Bobafett as a game input command.

Getting Started

Only two things are necessary to be up and running with triggers:

  1. Define your triggers in a lich_triggers: section in your Charname-setup.yaml or set them up in a separate file named Charname-triggers.yaml. Both ways are supported.
  2. Run the trigger-watcher script. It's recommended to add this to your autostarts.

Some Finer Details

Within a single trigger name, you can specify multiple triggers and multiple responses. Here are some simple examples:

# LICH TRIGGERS
# Yaml for Lich-based triggers

lich_triggers:
  wave_hello:
    triggers:
      - Bobafett just arrived
      - Hansolo just arrived
    responses:
      - wave

Here we've specified two different triggers, and modified our response slightly. If Lich sees game output of either Bobafett just arrived OR Hansolo just arrived, it will trigger a response of game input wave.

# LICH TRIGGERS
# Yaml for Lich-based triggers

lich_triggers:
  wave_hello:
    triggers:
      - Bobafett just arrived
    responses:
      - wave Bobafett
      - kick Bobafett

Here we've specified a single trigger but two responses. If Lich sees Bobafett enter the room, it will send the two responses as game commands one right after the other: wave Bobafett and kick Bobafett.

Trigger Commands

A set of trigger commands is provided for use by the user. These commands tell Lich to treat trigger responses in various ways. Commands are always preceded by the = sign, which tells Lich to run it as a trigger command. Trigger response lines must begin with these commands to be properly parsed. With the exception of the elsif/else parts of if functionality, trigger commands cannot be embedded. However, since multiple trigger response lines are supported, just add additional response lines if you want to use additional trigger commands. NOTE: Enclose all trigger command responses in curly braces.

Trigger Command Description Example Notes
=exec Execute a script. =exec {<script> [arguments]}
=execw Execute a script and wait to complete the script before moving on. =execw {<script> [arguments]}
=eval Evaluate as Lich/Ruby code. =eval {DRC.message("hello")}
=uvar Set or delete a Lich User Variable. =uvar set MyUserVariable 22
=uvar delete MyUserVariable
You can reference a stored user variable in your triggers via $UserVarName. Can embed simple Lich/Ruby code, enclosed in curly braces to be evaluated.
=logf Log to a file. =logf {<filename> <text to log>} Creates the file if it doesn't exist. Appends the data if it does. Includes a timestamp. Can embed simple Lich/Ruby code, enclosed in curly braces, to be evaluated before logging.
=logw Log to a window. =logw {<windowname> <text to log>} Creates the window if it doesn't exist. Includes a timestamp. Can embed simple Lich/Ruby code, enclosed in curly braces, to be evaluated before logging.
=pause Pause a specified amount of seconds. =pause {10}
=if
=elsif
=else
Allow if, elsif, else functionality. =if {3 > 2} {DRC.message("true")} =else {DRC.message("else")} Can have multiple actions for each if/elsif/else. Can have multiple elsif's. Nesting of these commands not currently supported, but valid Ruby works within them. Note the use of curly brackets, which are required.
=reload Reloads yaml trigger settings without killing the script. =reload Takes no arguments. An easy way to use this is to set up a trigger that uses this command when it sees a known phrase. Then type in game echo <the phrase to trigger>. Set it to an alias for even easier use.

Regex and Regex/UserVar Substitutions

Triggers support regex and trigger responses support regex substitutions and Lich UserVar substitutions.

To use regex substitutions, use capture groups in your trigger and reference them via $1 through $9. You are limited to 9 regex substitutions. Named capture groups are not currently supported.

lich_triggers:
  regex_sub_test:
    triggers:
      - You are (.*) years old
    responses:
      - say I am $1
>age
You are one hundred twenty-seven years old.
Among Elves, you are considered an adult.
To the other races, you appear to be young.
You are aging at a normal rate for an Elf.
[Type AGE HELP for more options.]
>
[trigger-watcher]>say I am one hundred twenty-seven
You say, "I am one hundred twenty-seven."

NOTE: If you're using regex substitution, use only a single trigger response.

To use UserVar substitutions, use the same $ character and reference the UserVar by its name. The first character in a UserVar cannot be a number. Otherwise, no limitations on the name.

lich_triggers:
  uservar_test:
    triggers:
      - What are your friend's names?
    responses:
      - say My friends are $friends # friends is a UserVar
Dilbert asks, "What are your friend's names?"
>
[trigger-watcher]>say My friends are ["Bob", "Bill", "Bernie", "Beatrice"]
You say, "My friends are ["Bob", "Bill", "Bernie", "Beatrice"]."

Advanced Trigger Usage

With use of the =eval trigger command, as well as embedded code evaluation in trigger responses, you can structure advanced triggers. If desired, you can also specify each individual trigger to only fire if one of a list of specified scripts is running by way of a use_with_scripts section in the trigger, or to NOT fire if one of a list of specified no_use_scripts is running. Triggers will always favor a decision based on no_use_scripts, meaning if any of the no_use_scripts are running, the trigger will not fire even if one of the use_with_scripts is running.

With advanced trigger responses, you will need to wrap the entire response line in quotes and then be careful about nested quotes. The use of Visual Studio code is recommended here, as it will visually color the line to let you know whether it's accepting it as a single trigger response string.

The following advanced trigger example tries to teach anyone who enters the room, but only if script afk is running, and only if their name is included in your UserVars.friends user variable, and only if you're currently in room 1234.

teach_friends:
    use_with_scripts:
      - afk
    triggers:
      - 'Also here: (\w+)'
    responses:
      - '=eval {UserVars.RoomOccupants.each {|name| fput("teach #{name}") if UserVars.friends.include?(name) && Room.current.id == 1234}}'
Clone this wiki locally