Extensions - lynnpye/sl_triggers GitHub Wiki
An SLT extension is deeper integration with SLT, allowing it to manage your trigger and settings data, manage your extension's SLT lifecycle, and provide more performant and deeper access to SLT functionality.
Scripting
An extension requires setting up a Quest and an .esp, with the Quest being your typical 'Start Immediately/Run Forever/Don't Show the Player' style of mod data quest. You will create a script that extends the sl_triggersExtension
script. The following are key features to pay attention to when creating your extension script:
- Your extension script will need to be attached to the Quest.
- The base Extension script requires the
PlayerRef
property be filled - Functions/Properties/Events of interest:
- string GetExtensionKey()
- OVERRIDE REQUIRED
- This will be a unique key to identify your extension; it will be used for file names so must comply with those restrictions
- string GetFriendlyName()
- OVERRIDE RECOMMENDED
- This provides a human friendly name for your extension. Without it, the extension key will be used (yuck).
- SLTInit()
- EXECUTION REQUIRED IN
OnInit()
- You must call this in your Quest's
OnInit()
for SLT to be able to set up integration with the extension.
- EXECUTION REQUIRED IN
- SLTReady()
- OVERRIDE OPTIONAL
- This function will be called when SLT is marked Ready after start
- Event OnSLTSettingsUpdated()
- OVERRIDE OPTIONAL
- Override this event if you want to receive notification when the MCM is closed (i.e. data refresh)
- bool Property IsEnabled
- Returns false if either or both of the extension itself or SLT generally are disabled; true if both are enabled; setting it only affects the extension
- Setting it changes the status but the implementation of selective re-registration of events was done in the SLT extension examples but isn't specifically part of the tech offered by the SLT extension framework.
- string[] Property TriggerKeys
- Automatically populated list of the extension trigger keys.
- When your event fires, iterate your trigger keys and check attributes to determine which trigger's scripts to run.
- int ActorRace(Actor)
- Returns: 1 if the Player, 2 if Undead, 3 if an NPC, 4 otherwise
- string RequestCommand(Actor, string script)
- Will run the requested script on the targeted Actor
- string FN_T(string _triggerKey)
- Returns the filename for the given trigger in context of your extension; used in JsonUtil calls
- string FN_S()
- Returns the filename for the extension settings file; used in JsonUtil calls
- string GetExtensionKey()
The remaining _slt_*
functions are meant for internal use; override or call them at your own peril. :)
To get/set settings or trigger data in your extension:
- Use FN_S() or FN_T(triggerKey from TriggerKeys) to create the filename
- Use JsonUtil functions directly
- You can also create wrappers and variables to avoid having to write out non-verifiable strings over and over again.
Attributes JSON
An extension should also have an <extensionKey>-attributes.json
file which should be shipped in Data\SKSE\Plugins\sl_triggers\extensions\
. This file tells SLT what trigger and settings attributes the extension cares about, including type information, default values, and what sort of MCM widget it will need. You can also provide a minor "trigger" layout, including different layouts based on a key attribute.
Here is a (mangled JSON, you can't drop this in and use it) commented copy of the sl_triggersExtensionCore-attributes.json file:
{
# this is not a real JSON comment, as there is no such thing
# I am adding these '#' hash comments to explain the JSON
# First, "comment_section" is not a real section that SLT looks for.
# I include it here to do two things.
# One, to describe how the comment strings work, i.e. if a string starts with an '#', it's a comment
# Two, to show that sections with names SLT doesn't care about are ignored, so if this
# helps you organizationally, go for it.
"comment_section" : [
["# this is an attributes file for an SLT extension"],
["# it will also contain this structure, letting SLT know what attributes to track"],
["# this is a comment because it starts with an octothorpe(#)"],
["# otherwise this file has to be proper json"],
["# it is divided into a 'triggers' block and a 'settings' block"],
["# the blocks are otherwise the same, following the same naming convention"]
],
# It's JSON, so member order of these top level elements doesn't really matter.
# That said, this section marks the beginning of the "trigger" attributes block.
# Further down is a section for the "settings" attributes block.
# Most extensions will have a trigger block but not all will have a settings block.
# "trigger_attributes" consists of a list of 2-element string lists (comments are of course always ignored)
# the first element is the attribute name and should match what you use in code to look up
# attribute values
# the second element is the section name further down that contains the data about the attribute
"trigger_attributes" : [
["# this section is just a simple list of attributes"],
["# one per row, yes"],
["event", "triggerattribute_event"],
["keymapping", "triggerattribute_keymapping"],
["modifierkeymapping", "triggerattribute_modifierkeymapping"],
["usedak", "triggerattribute_usedak"],
["chance", "triggerattribute_chance"],
["do_1", "triggerattribute_do_1"],
["do_2", "triggerattribute_do_2"],
["do_3", "triggerattribute_do_3"]
],
# This is an example of an attribute descriptor section, in this case for "event"
"triggerattribute_event" : [
["# here is where you define aspects of the attribute"],
# an 'int'
["type", "int"],
# MCM will display as menu, labeled "Event: ", defaulting to index 0, with the last strings providing the menu selections
["widget", "menu", "Event: ", "0", "- Select an Event -", "Key Mapping", "Top of the Hour"],
# all attributes can have an "info" descriptor that will show up when hovering in the MCM
["info", "Choose which type of awesome event this trigger will use."]
],
"triggerattribute_keymapping" :
[
["type", "int", "-1"],
["#", "widget", "keymapping", "label", "default index" ],
# MCM will display as a keymapping, labeled "Key: ", with a default value of -1 (unmapped)
["widget", "keymapping", "Key: ", "-1"],
["info", "Choose the key to map to the action."]
],
"triggerattribute_modifierkeymapping" :
[
["type", "int", -1],
["widget", "keymapping", "Modifier Key: ", "-1"],
["info", "(Optional) If specified, will be required to be pressed to trigger the action."]
],
"triggerattribute_usedak" : [
["type", "int"],
# A toggle widget, pretty simple
["widget", "toggle", "Use DAK? ", "0"],
["info", "(Optional) If enabled, will use the Dynamic Activation Key instead of the Modifier key (if selected)"]
],
"triggerattribute_chance" : [
["type", "float", "0.0"],
# a slider widget, which can work with either an int or a float
# This widget is labeled "Chance: ", runs from 0.0 to 100.0 in increments of 1.0, and uses the format string {0} which removes all decimals
["widget", "slider", "Chance: ", "0.0", "100.0", "1.0", "{0}"],
["info", "The chance the trigger will run when all prerequisites are met."]
],
"triggerattribute_do_1" : [
["type", "string"],
# "command" is a special menu widget specifically to display the script list
["widget", "command", "Command 1: "],
["info", "You can run up to 3 commands associated with this keymapping. This is the first."]
],
"triggerattribute_do_2" : [
["type", "string"],
["widget", "command", "Command 2: "],
["info", "You can run up to 3 commands associated with this keymapping. This is the second."]
],
"triggerattribute_do_3" : [
["type", "string"],
["widget", "command", "Command 3: "],
["info", "You can run up to 3 commands associated with this keymapping. This is the third."]
],
# "trigger_layoutconditions" is an optional section
# if specified, the first "functional" item should be a 1-element string list containing the
# attribute name which will be checked on a trigger by trigger basis to determine which layout to use
"trigger_layoutconditions" : [
["# checked in sequence shown, first match determines layout selection"],
# in this case we are keying on event, so depending on which event you pick, a different layout will be used
["event"],
# if event has value 1, the trigger should use the triggerlayout_keymapping layout
["1", "triggerlayout_keymapping"],
# if event has value 2, the trigger should use the triggerlayout_topofthehour layout
["2", "triggerlayout_topofthehour"]
# I know that these are the only two possible values, but if it *did* have a different value, then
# SLT would default to the triggerlayout layout
],
# A layout is described as a list of 1 or 2 element string lists
# Triggers will be laid out 5 per page vertically, each being
# laid out in the MCM using the LEFT_TO_RIGHT method.
# Each string list will be one row. Each string should be an attribute
# name. Leaving a string empty ("") will leave that spot empty
# You do not have to specify the second spot if it will be empty.
"triggerlayout_keymapping" : [
["event", "keymapping"],
["modifierkeymapping", "usedak"],
["do_1", "do_2"],
["do_3"]
],
# these special layout sections are also optional, based on whether you used "trigger_layoutconditions"
"triggerlayout_topofthehour" : [
["event", "chance"],
["do_1", "do_2"],
["do_3"]
],
# This section, however, is required. It is the default layout. In this case the intent is to
# force you to pick an event before showing more attributes.
"triggerlayout" : [
["event"]
]
,
# And this brings us to the settings block. It is pretty much identical to the triggers block
# in terms of it's sections and their uses.
# Key differences:
# Extension settings have their own special page
# Extension settings are on a single page, not on a repeated card
# Extension settings are stored in Data\SKSE\Plugins\sl_triggers\extensions\<extensionKey>-settings.json
# Trigger settings are stored in Data\SKSE\Plugins\sl_triggers\extensions\<extensionKey>\<triggerfiles.json>
"settings_attributes" : [
["testtoggle"]
],
"settingsattribute_testtoggle" : [
["type", "int"],
["widget", "toggle", "Test Toggle? ", 1],
["info", "This is testtoggle. Isn't he cute?"]
],
# Note the lack of a "settings_layoutconditions" but we still need a "settingslayout" as a default
"settingslayout" : [
["testtoggle"]
]
}
What Else?
Beyond this, everything else is mostly up to you. In your extension you will be responsible for registering for your mod's events, capturing them, iterating the triggers and checking conditions to know which, if any, to fire, and finally, sending the request to SLT. Take a look at how sl_triggersExtensionCore
and sl_triggersExtensionSexLab
are developed to get my take on a few things to make extension authoring a little easier.