Skip to content

Trigger Application

Idle edited this page Feb 2, 2026 · 1 revision

The module exposes to the global the parent classes that must be inherited to populate the different parts of a trigger application. They are available before any of the module's hooks are called (the module being flagged as library).

globalThis.triggerEngine = {
  NodeEntry,
  NodeField,
  TriggerHook,
  TriggerNode,
};

To register a trigger application, you must use the following hook, which has the register function as its only argument.

Hooks.once("triggerEngine.registerApplication", (registerApplication) => {
  registerApplication("<module-id>", "<application-id>", applicationData);
});

The applicationData object contains the different parts that define your application.

type ApplicationData =  {
   convertors?: EntryConvertor[];
   entries?: (typeof NodeEntry)[];
   hooks?: (typeof TriggerHook)[];
   nodes?: (typeof TriggerNode)[];
   builtins?: (BuiltInOptions | true);
   mode?: "setting" | "free";
   setting?: ApplicationMenuOptions;
}

Convertor

Entry convertors are used to automatically convert one entry type to another. As long as a convertors exists for the specified types, the user will be able to drag a connection between them without needing to do anything else.

type EntryConvertor<TInput extends any = any, TOutput extends any = any> = {
    output: string;
    input: string;
    convertToInput: (value: TOutput, userContext: UserPF2e) => Promise<TInput> | TInput;
};

Node Entry

https://github.com/reonZ/trigger-engine/blob/master/src/engine/entry/entry.ts

Nodes are comprised of entries of 2 distinct types:

  • The first are bridge entries which dictate the chain of execution of nodes
  • The second are type entries, those entries represent data that is incoming to (inputs) or outgoing from (output) the node. Connected entries move data dynamically at runtime.

Entry Field

https://github.com/reonZ/trigger-engine/blob/master/src/engine/entry/field/field.ts

Some input entries can have a field allowing for static input directly at build time.

Trigger Hook

https://github.com/reonZ/trigger-engine/blob/master/src/engine/hooks/hook.ts

Trigger hooks are what runs in the background, they are what decides which trigger is executed and when.

Caution

The TriggerHook#_disabled must disable anything that is currently running for the hook and will be called every time triggers are saved before calling the TriggerHook#_enabled. So make sure you handle it properly to avoid hooks or wrappers to be registered multiple times.

Tip

Trigger hooks are not in play with free applications.

Trigger Node

https://github.com/reonZ/trigger-engine/blob/master/src/engine/node/node.ts

Trigger nodes are the backbone of triggers, they define logic blocks and are executed in succession. The module allows for non-executable nodes (no bridge entries), in which case their output will be queried from the currently executed node instead.

Builtins

The module comes bundled with a few of each parts already implemented, they can be added at will to any application, either as a whole (by setting applicationData.builtins to true) or by specifying the ones needed in each section.

type BuiltInOptions = {
{
    entries?: ("number" | "boolean" | "any" | "item" | "target" | "text" | "user")[];
    convertors?: ("item-to-target" | "text-to-number" | "number-to-text" | "user-to-target" | "target-to-user")[];
    hooks?: (
        "create-combatant-hook" | "create-token-hook" | "delete-combatant-hook" | "delete-token-hook" |
        "execute-hook" | "move-token-hook" | "region-hook" | "test-hook"
    )[];
    nodes?: (
        "execute-event" | "await-confirm" | "console-log" | "create-message" | "delete-item" |
        "execute-script" | "update-item" | "if-truthy" | "is-combatant" | "list-contains" |
        "create-combatant-event" | "create-token-event" | "delete-combatant-event" | "delete-token-event" |
        "move-token-event" | "region-event" | "test-event" | "extract-actor" | "extract-item" |
        "actors-match" | "break-loop" | "compare-numbers" | "filter-targets" | "format-text" |
        "resolve-formula" | "texts-match" | "split-boolean" | "split-number" | "split-text" |
        "current-combatant" | "scene-targets" | "user-value"
    )[];
}}

Tip

Each section can be set to true to add all builtin entries for that particular section.

Important

A lot of those parts are interconnected, adding one node without adding the entries that it uses for instance, will break the node and render your trigger invalid.

Application Mode

If you want to make use of the blueprint menu without all the automation and settings associated to an application, You must declare it as a free application instead of the default setting.

Setting Menu Localization

Just like in a lot of other places, the module allows you to localize your application, by default, it will always look for a specific predefined path but you can also override it by providing either a string or a localization key.

This section allow you to override the setting menu localization paths. The default path for each property is as follow: <module-id>.<application-id>.setting.<property>

type ApplicationMenuOptions = {
    hint?: string;
    icon?: string;
    label?: string;
    name?: string;
};

Tip

This does nothing for free applications.

Clone this wiki locally