Skip to content

Triggernometry FAQ and examples

Paissa Heavy Industries edited this page Sep 25, 2023 · 43 revisions

This page includes some answers to commonly asked questions, examples on how to use Triggernometry, and some tips and tricks for more advanced usage. Please note that this information is relevant to the latest plugin version only - some functions and properties may not be available at all on older versions.

Current documentation version: v1.1.7.4

Table of Contents

Reference

Expressions

Whenever you see Triggernometry refer to a numeric expression, that means that the result of the expression and the expression itself will be considered a mathematical expression, and its result will be evaluted. For example, the result of a numeric expression like 2+2 would be 4. Triggernometry will parse a numeric expression of virtually any complexity, and you can refer to variables and event text named capture groups in the expression as well.

String expressions, however, do not go through such evaluation; while variables and capture groups can of course be referred to for substitution and such, mathematical functions and constants such as cos(x) do not get evaluated.

It's however possible to have parts of a string expression be evaluated as numeric, or vice versa, by using these two:

${numeric:xxx} = will cause "xxx" to be evaluated as a numeric expression and then substituted
${string:xxx} = will cause "xxx" to be evaluated as a string expression and then substituted

Arithmetic operations

^ = power
% = remainder (for modulo, see numeric function mod)
: = division
/ = division
* = multiplication
+ = addition
- = substraction

Logical operators

Results of these operators are numeric; 1 for true, 0 for false.

> = greater than
≥ = greater or equal than
< = less than
≤ = less or equal than
= = equal
≠ = not equal

Numeric constants

pi = 3.1415...
pi2 = 6.2831... (pi * 2)
pi05 = 1.5707... (pi / 2)
pi025 = 0.7853... (pi / 4)
pi0125 = 0.3926... (pi / 8)
pitorad = 57.2957... (for radian/degree conversions)
piofrad = 0.0174... (for radian/degree conversions)
e = 2.7182... (base of the natural logarithm)
phi = 1.6180... (golden ratio)
major = 0.6180... (fibonacci ratio to next value)
minor = 0.3819... (fibonacci ratio to second next value)

Triggernometry constants

Triggernometry's own constants can be manipulated through the configuration editor, but the following constants are provided out of the box (default values included):

TelestoEndpoint = localhost
TelestoPort = 51323
OBSWebsocketEndpoint = localhost
OBSWebsocketPort = 4455

Triggernometry version is also available as constants, and they are overwritten on every load regardless of what you change them to in the configuration. For example, version number 1.2.3.4 would be available as:

TriggernometryVersionMajor = 1
TriggernometryVersionMinor = 2
TriggernometryVersionBuild = 3
TriggernometryVersionRevision = 4

Mathematical functions

abs(x) = absolute value of x
cos(x) = cosine of x
cosh(x) = hyperbolic cosine of x
arccos(x) = arccosine of x
cosec(x) = cosecant of x
sin(x) = sine of x
sinh(x) = hyperbolic sine of x
arcsin(x) = arcsine of x
sec(x) = secant of x
tan(x) = tangent of x
tanh(x) = hyperbolic tangent of x
arctan(x) = arctangent of x
arctan2(x, y) = arctangent of x and y
cotan(x) = cotangent of x
distance(x1, y1, x2, y2) = distance between the two given points
radtodeg(x) = converts radians to degrees
degtorad(x) = converts degrees to radians
max(...) = the largest of the specified numbers
min(...) = the smallest of the specified numbers
random(x, y) = random integer that is greater or equal to x, and less than y (x >= ... < y)
sqrt(x) = square root of x
root(x, y) = the yth root of x (root(25, 2) = square root of 25, root(125, 3) = cube root of 125)
rem(x, y) = remainder of the division of x and y (IEEE standard remainder, not the same thing as modulus)
pow(x, y) = x to the power of y
exp(x) = e to the power of x
log(x) = natural (base e) logarithm of x
log(x, y) = base y logarithm of x
round(x) = rounds x to the nearest integer
round(x, y) = rounds x with y decimals
floor(x) = rounds down the previous integer
ceiling(x) = rounds up the next integer
sign(x) = returns the sign of x (-1 = x is negative, 0 = x is zero, 1 = x is positive)
hex2dec(x) = converts a base16 (hex) number to base10 (decimal)
hex2float(x) = converts a base16 (hex) number to a single-precision floating point number
hex2double(x) = converts a base16 (hex) number to a double-precision floating point number
or(...) = returns 1 if any of the specified numbers is other than zero, 0 if not
and(...) = returns 1 if all of the specified numbers are other than zero, 0 if not
if(x,y,z) = returns y if x is non-zero, z if x is zero (for example, if (2>3,4,5) would return 5
mod(x, y) = modulus of x divided by y

String functions

To use these functions, the calling convention is ${func:function name(any parameters):value}. If a function doesn't take value, it doesn't matter if you call it as toupper or toupper(). For example, if you wanted to change "hello" to uppercase, you could do ${func:toupper:hello}.

toupper = changes given value to uppercase
tolower = changes given value to lowercase
length = returns the length of the value
dec2hex = converts the given base 10 value to its base 16 representation
dec2hex2 = converts the given base 10 value to its base 16 representation (outputting 2 bytes)
dec2hex4 = converts the given base 10 value to its base 16 representation (outputting 4 bytes)
dec2hex8 = converts the given base 10 value to its base 16 representation (outputting 8 bytes)
float2hex = converts a single-precision floating point number to its 32-bit base 16 presentation
double2hex = converts a double-precision floating point number to its 64-bit base 16 presentation
padleft(character code, length) = pads the value to the given length from the left side with the character defined by the character code
padright(character code, length) = pads the value to the given length from the right side with the character defined by the character code
substring(start) = returns a substring of value starting from the given index
substring(start, length) = returns a substring of value starting from the given index and containing up to the given length of characters
indexof(string) = returns the index of the search string within the given value
lastindexof(string) = returns the last index of the search string within the given value
trim = trims whitespace from both sides of the value
trim(character code, character code, ...) = trims the given characters from both sides of the value
trimleft = trims whitespace from the left side of the value
trimleft(character code, character code, ...) = trims the given characters from the left side of the value
trimright = trims whitespace from the right side of the value
trimright(character code, character code, ...) = trims the given characters from the right side of the value
format(type, format string) = formats the value to the .Net type and specified format string, for example: ${func:format(System.Double,"0.0"):1213131}
compare(stringtocompare) = case sensitive string compare (-1 = string lower in alphabetical order, 0 = strings equal, 1 = string higher)
compare(stringtocompare, ignorecase) = case (in)sensitive string compare (-1 = string lower in alphabetical order, 0 = strings equal, 1 = string higher)
utctime = convert an UNIX timestamp into default date/time string format in UTC time
utctime(format) = convert an UNIX timestamp into the given date/time string format in UTC time
localtime = convert an UNIX timestamp into default date/time string format in your local time
localtime(format) = convert an UNIX timestamp into the given date/time string format in your local time

Markup reference

${xxx} = retrieve value of regular expression named capture group xxx, or the nth capture group if xxx is numeric and no named group with same name exists
${numeric:xxx} = evaluate xxx as a numeric expression
${string:xxx} = evaluate xxx as a string expression
${var:xxx} = retrieve variable xxx value 
${evar:xxx} = check if variable xxx exists (1 = exists, 0 = doesn't exist)
${lvar:xxx.size} = returns the size of the list variable xxx
${lvar:xxx.indexof(hello warudo)} = searches for the given string (in this example, "hello warudo"), and returns the index number of the first instance found, or zero if not found
${lvar:xxx.lastindexof(hello warudo)} = searches for the given string (in this example, "hello warudo"), and returns the index number of the last instance found, or zero if not found
${lvar:xxx[1]} = retrieves the value in the 1st index of the list variable xxx
${lvar:xxx[2]} = retrieves the value in the 2nd index of the list variable xxx
${lvar:xxx[last]} = retrieves the value in the last index of the list variable xxx
${elvar:xxx} = check if list variable xxx exists (1 = exists, 0 = doesn't exist)
${tvar:xxx.width} = returns the width (number of columns) of the table variable xxx
${tvar:xxx.height} = returns the height (number of rows) of the table variable xxx
${tvar:xxx[2][4]} = retrieves the value in the 2nd column, 4th row of the table variable xxx
${tvar:xxx[last][2]} = retrieves the value in the last column of row 2 of the table variable xxx
${tvar:xxx[2][last]} = retrieves the value in the last row of column 2 of the table variable xxx
${tvarcl:xxx[My Column Name][3]} = performs a column based lookup (first row is header row), and retrieves the value of the 3rd row of that column
${tvarrl:xxx[My Row Name][2]} = performs a row based lookup (first column is header column), and retrieves the value of the 2nd column of that row
${etvar:xxx} = check if table variable xxx exists (1 = exists, 0 = doesn't exist)
${func:xxx(yyy):zzz} = execute function xxx (with arguments yyy, if any are needed) on value zzz

To reference persistent variables, the same markup as above works, with an extra "p" in the names.

${pvar:xxx} = retrieve persistent variable xxx value 
${epvar:xxx} = check if persistent variable xxx exists (1 = exists, 0 = doesn't exist)
${plvar:xxx.size} = returns the size of the persistent list variable xxx
${plvar:xxx.indexof(hello warudo)} = searches for the given string (in this example, "hello warudo"), and returns the index number of the first instance found, or zero if not found
${plvar:xxx.lastindexof(hello warudo)} = searches for the given string (in this example, "hello warudo"), and returns the index number of the last instance found, or zero if not found
${plvar:xxx[1]} = retrieves the value in the 1st index of the persistent list variable xxx
${plvar:xxx[2]} = retrieves the value in the 2nd index of the persistent list variable xxx
${plvar:xxx[last]} = retrieves the value in the last index of the persistent list variable xxx
${eplvar:xxx} = check if persistent list variable xxx exists (1 = exists, 0 = doesn't exist)
${ptvar:xxx.width} = returns the width (number of columns) of the persistent table variable xxx
${ptvar:xxx.height} = returns the height (number of rows) of the persistent table variable xxx
${ptvar:xxx[2][4]} = retrieves the value in the 2nd column, 4th row of the persistent table variable xxx
${ptvar:xxx[last][2]} = retrieves the value in the last column of row 2 of the persistent table variable xxx
${ptvar:xxx[2][last]} = retrieves the value in the last row of column 2 of the persistent table variable xxx
${ptvarcl:xxx[My Column Name][3]} = performs a column based lookup (first row is header row), and retrieves the value of the 3rd row of that column
${ptvarrl:xxx[My Row Name][2]} = performs a row based lookup (first column is header column), and retrieves the value of the 2nd column of that row
${eptvar:xxx} = check if persistent table variable xxx exists (1 = exists, 0 = doesn't exist)

Special variables

Triggernometry generates several special variables, which can be used in your conditions, callouts and such. They are used as any other reference (for example, ${_since}). The list is as follows:

_duration = Duration of the current encounter in ACT in seconds, returns 0 if there is no active encounter
_event = Full event text which fired the trigger (only valid in the context of that specific trigger)
_incombat = Flag determining if there is an active combat encounter (0 = not in combat, 1 = currently in combat)
_since = Number of seconds since the trigger fired (only valid in the context of that specific trigger)
_sincems = Number of milliseconds since the trigger fired (only valid in the context of that specific trigger)
_triggerid = Unique ID of the trigger (only valid in the context of that specific trigger)
_triggername = Name of the trigger (only valid in the context of that specific trigger)
_timestamp = UNIX timestamp of the moment the trigger fired (only valid in the context of that specific trigger)
_timestampms = UNIX timestamp of the moment the trigger fired (only valid in the context of that specific trigger) in milliseconds
_systemtime = UNIX timestamp of the current system time
_systemtimems = UNIX timestamp of the current system time in milliseconds
_zone = Name of the zone the trigger fired in (only valid in the context of that specific trigger)
_response = Response received from a remote endpoint (only valid in the context of that specific action)
_jsonresponse[path] = Response received from a remote endpoint (only valid in the context of that specific action), parsed as JSON (for example, you can do _jsonresponse[firstnode/secondnode/myitem])
_screenwidth = The width of the primary monitor in pixels
_screenheight = The height of the primary monitor in pixels
_lastencounter = ACT DPS information from the last encounter in default miniparse format
_activeencounter = ACT DPS information from the ongoing encounter in default miniparse format
_env[varname] = Retrieves the value of the specified environment variable
_const[varname] = Retrieves the value of a constant specified in Triggernometry configuration
_responsecode = HTTP status code populated by the last JSON operation in the context of the trigger fire (n = status code, 0 if no operation executed, -1 on failure)
_loopiterator = Number of iterations a loop has gone through, zero-based (= zero on first run)

In the context of auras, you will also have access to these variables to refer to the current parameters of the aura (valid within the context of that aura only):

_x = Current X position
_y = Current Y position
_w or _width = Current width
_h or _height = Current height
_opacity = Current opacity in % (0 ... 100)

You will also be able to access properties of other auras by using these special variables:

_textaura[id].property = Allows you to access properties on the text aura with the given id (available properties: x, y, w, h, opacity, text)
_imageaura[id].property = Allows you to access properties on the image aura with the given id (available properties: x, y, w, h, opacity)

Game-specific special variables

Final Fantasy XIV

_ffxivparty = A special array-type variable that represents current party members (up to eight players)
_ffxiventity = A special array-type variable that represents all currently known entities in the game world
_ffxivplayer = Shorthand for your name of your currently logged in character, superceded by _ffxivparty but still maintained for backward compatibility
_ffxivtime = Gives you the current Eorzean time in minutes (0 = midnight, 720 = noon)
_ffxivpartyorder = Provides some debug information about the current party order
_ffxivprocid = Process ID of the instance FFXIV ACT plugin is listening to
_ffxivprocname = Process name of the instance FFXIV ACT plugin is listening to
_ffxivzoneid = ID of the current ingame zone

Both _ffxivparty and _ffxiventity can be addressed through several methods:

_ffxivparty[1] = first party member in the list
_ffxiventity[1] = first actor in the combatant list
_ffxivparty[My Name] = party member with the given name
_ffxiventity[Titan] = actor with the given name in the combatant list (returns the first one found, if multiple exist)
_ffxivparty[1234ABCD] = party member with the given ID
_ffxiventity[1234ABCD] = actor with the given ID in the combatant list

Properties available in _ffxivparty and _ffxiventity:

name = Name of the actor
job = Current job as a three letter acronym (AST, CUL, MIN, SMN, etc)
jobid = Numeric (internal ingame) representation of the current job
currenthp, currentmp, currentcp, currentgp = Current HP, MP, CP, and GP
maxhp, maxmp, maxcp, maxgp = Maximum HP, MP, CP, and GP
level = Level of the actor
x, y, and z = Position information of the actor
inparty = Party membership flag (0 = character doesn't exist or is not in party, 1 = character exists and is in the party)
id = Hexadecimal ID of the actor
order = Order number of the actor inside the _ffxivparty structure (this property is 0 for _ffxiventity)
worldid = Numeric ID of the home world of the character
worldname = Name of the home world of the character
currentworldid = Numeric ID of the current world of the character
heading = Heading of the entity in radians (South 0, North ±PI)
targetid = Hexadecimal ID of the actor currently selected as target (0 is there is no target selected)
casttargetid = Hexadecimal ID of the actor the current cast is targetting (0 is there is no cast in progress)
distance = Distance from the main combatant (you) to the entity
role = Role of the actor ("Tank", "Healer", "DPS", "Crafter", "Gatherer", or blank if not available)

Action types

  • System beep = Generates a beep sound using the PC speaker or audio device. The frequency and the length of the beep are configurable.
  • Play sound file = Plays a sound file. Volume is configurable, and sound files can also be set to use an exclusive player per sound, so they don't end up queueing. By default Triggernometry handles its own sounds and bypasses ACT configuration, but in case you are using other sound-related plugins, you can set Triggernometry to go through ACT anyway with the "Use ACT for playing sound files" setting in the plugin configurations screen.
  • Use text-to-speech = Use TTS to have call out something. Volume and speed of speech can be configured, and speech can also be set to use an exclusive synthesizer per line, so speech doesn't end up queueing. By default Triggernometry uses the system TTS and bypasses ACT configuration, but in case you are using other speech-related plugins, you can set Triggernometry to go through ACT anyway with the "Use ACT for text-to-speech" setting in the plugin configurations screen. Enabling this will make the speech speed option ineffective.
  • Launch process = Launches an external process with the given command-line parameters. ''Please be aware that there is a risk you may cause damage to your computer with this option - use only when you are sure of what you are doing!''
  • Trigger operation = Allows you to perform actions related to triggers. It's possible to enable and disable a trigger, fire another trigger with custom text, and to cancel all queued actions from a specified trigger. The latter option is useful if you have many delayed actions queued from other triggers. Using the option to fire a trigger features options for bypassing conditions and checks; for example, you can force fire a trigger while ignoring its conditions, while still processing its regular expression.
  • Send keypresses to active window = Sends keypresses to active window. Before you ask, the feature is not reliable or accurate enough to make the game play itself for you; the resolution of the timers and the general unreliability of the mechanism means it's unusable for that purpose. For example, simply using the keyboard yourself at the same time is enough to throw the keypresses off. It's possible to send the keypress through SendKeys, or as a window message. The format of the SendKeys keypress string is described here: https://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
  • Execute script = Executes a C# script. The script code will be compiled on every execution, and then executed if successful. You'll also have to manually supply a comma-separated list of all the assemblies to reference, although Triggernometry will autoreference whatever assemblies are already loaded in. ''Please be aware that there is a risk you may cause damage to your computer with this option - use only when you are sure of what you are doing!''
  • Show message box = Just displays a message box with an OK button.
  • Scalar variable operation = Allows you to manipulate scalar variables. If you unset a variable, it will be removed from Triggernometry's variable pool completely. Setting a variable value by string expression will expand all references to variables and named capture groups, but nothing further than that. The result of a string expression like "2+2" will simply be "2+2". With numeric expressions, after variables and named capture groups have been expanded, the expression will be evaluated as a mathematical expression. This means that the result of a numeric expression like "2+2" would be "4".
  • Image aura operation = Image auras are essentially images displayed on your screen, making it possible to implement visual triggers. The aura system works by unique identifiers, where one instance of an image has an unique name. This allows you to refer to a specific image, when you want to adjust its position or other parameters.
  • Folder operation = This operation makes it possible to enable or disable a whole folder of triggers. The operation is always implicitly recursive, as disabling any parent folder means any child folders and triggers are also disabled even if they might not be explicitly disabled.
  • End encounter = Simply ends the encounter on ACT side. Can be useful if you want to have a trigger break the encounter.
  • Discord webhook = Send a message to Discord through a Discord webhook. The message can optionally be sent as a TTS message. Message length is limited to 2000 characters.
  • Text aura operation = Text auras are essentially text displayed on your screen, making it possible to implement countdowns, text messages, and such. The aura system works by unique identifiers, where one instance of a text block has an unique name. This allows you to refer to a specific text block, when you want to adjust its position or other parameters.
  • Log message = Allows you to log something into Triggernometry's own log, or emit a log line that will be processed like it came from ACT itself. Can be useful for debugging complicated triggers, or firing off triggers by constructing your own trigger phrase.
  • List variable operation = Provides list operations, such as insertion, removal, join. List variables exist separately from other variables, so it's possible to have for example a scalar variable and a list variable with the same name.
  • OBS remote control operation = Allows you to remote control OBS to start/stop recording, streaming, replay buffer, and switching scenes on the fly. Can be used to make triggers that auto-record pulls or certain parts of the fight, or switch scenes based on what's happening. Requires the installation of OBS Websocket plugin for OBS, available from: https://obsproject.com/forum/resources/obs-websocket-remote-control-of-obs-studio-made-easy.466. The endpoint and authentication are configurable.
  • Generic JSON operation = Allows you to send a JSON payload to an endpoint, and optionally fire a log line based on the response received from the endpoint.
  • Send window message = Provides the opportunity to send an arbitrary window message to a named window.
  • File operation = Allows you to read local or remote files into scalar, list, and table variables
  • Table variable operation = Provides table operations, such as resize, append. Table variables exist separately from other variables, so it's possible to have for example a scalar variable and a table variable with the same name.
  • Mutex operation = Makes it possible to have mutual exclusion control over trigger execution or a protected section inside a trigger.
  • Named callback = Allows you to register your code as callbacks into Triggernometry, and then have callbacks executed as Triggernometry actions.
  • Placeholder = Just a placeholder to comment code and organize longer and more complicated triggers.
  • Mouse operation = Enables you to move the mouse pointer and send mouse clicks.
  • Loop = Allows you to perform loops of actions, checking a condition in between each iteration.
  • Repository operation = Performs repository updates.

Scripting

The scripting action will allow you to run C# scripts, and all Triggernometry internals are generally welcome to be poked. Triggernometry will automatically add references in the script environment to all assemblies currently loaded in, so you generally don't need to add references to anything. Finally, Triggernometry will automatically do an implicit "using System;" for every script.

There's also TriggernometryHelpers in the global namespace, containing a bunch of useful states and methods for your leisure.

Security

The script environment can be pretty powerful, but some of the more spicy namespaces/APIs are restricted by default to be opt-in for users, so no one should accidentally end up running malicious scripts. Scripting security settings are under Triggernometry's configuration, where namespaces can be enabled individually, and separately for local and remote triggers. Also, if ACT running is running as admin, everything will be restricted by default due to elevation making potentially dangerous scripts a considerable bit more dangerous - users will be required to separately allow using APIs when running as admin.

Likewise, by default all remote repositories do not allow script execution, and users are required to explicitly allow script actions' execution on per-repository basis.

Properties (in TriggernometryHelpers)

Dictionary<string, object> Storage: A storage dictionary for scripts, that will persist and be visible across all scripts and their invocations. You can store script-relevant data such as connection handles and things you want to reuse in other scripts or at a later time here. Scripts are responsible for locking the dictionary themselves!

Triggernometry.RealPlugin Plugin: Instance of the actual Triggernometry plugin.

Triggernometry.Context CurrentContext: Instance of the trigger firing context in which the script was executed.

Methods (in TriggernometryHelpers)

void PlayTTS(string text)
void PlaySound(string uri)

Play TTS and sound, respecting Triggernometry's ACT hook settings.

string EvaluateStringExpression(string expr)
double EvaluateNumericExpression(string expr)

Evaluate expressions such like Triggernometry would on its own expression fields, so you could for example EvaluateStringExpression("${_ffxivplayer}").

string GetScalarVariable(bool persistent, string varname, string defValue = "")
void SetScalarVariable(bool persistent, string varname, string data)
VariableList GetListVariable(bool persistent, string varname)
void SetListVariable(bool persistent, string varname, VariableList data)
VariableTable GetTableVariable(bool persistent, string varname)
void SetTableVariable(bool persistent, string varname, VariableTable data)

Manipulate Triggernometry's variables. The first parameter determines if you're playing around with persistent variables or not.

string GetRegexMatch(int idx, string defValue = "")
string GetRegexMatch(string name, string defValue = "")

Retrieves a capture group from the regex match that fired the trigger (and ran the script), either by index or by name.

Examples

Show a message box with the player's name:

using System.Windows.Forms;
MessageBox.Show(TriggernometryHelpers.EvaluateStringExpression("${_ffxivplayer}"));

Say something in TTS if the current player name matches the one in the regular expression capture group:

if (!String.Compare(TriggernometryHelpers.GetRegexMatch("playername"), TriggernometryHelpers.EvaluateStringExpression("${_ffxivplayer}")))
{
    TriggernometryHelpers.PlayTTS("hi! it's me!");
}

Create a custom action and queue/execute it just like Triggernometry would do:

using Triggernometry;
Action a = new Action();
a.ActionType = Action.ActionTypeEnum.SystemBeep;
TriggernometryHelpers.Plugin.QueueAction(TriggernometryHelpers.CurrentContext, TriggernometryHelpers.CurrentContext.trig, null, a, System.DateTime.Now);

Open a WebSocket connection to remote host:

using WebSocketSharp;

WebSocket ws = new WebSocket(@"ws://127.0.0.1:4444");
ws.Connect();
ws.Send("hello world!");

Questions and examples

What are repositories?

Repositories are remote collections of triggers that can be added to Triggernometry. Triggernometry will then automatically update these repositories, giving you the latest updates and triggers for the latest content automatically, without having to do anything yourself.

A repository generally has a theme, for example a raid trigger repository would give you triggers related to raids, but not triggers related to dungeons. You can find more information about them at: https://github.com/paissaheavyindustries/Triggernometry/tree/master/Repositories

Can I change TTS in Triggernometry?

Yes - there are multiple options to control TTS output in Triggernometry. By default, Triggernometry handles TTS on its own, meaning it will use the system TTS. In this mode, if you would like to use another TTS, you would have to change the system TTS.

You can also let ACT take care of TTS. To achieve this, go to Options / Edit Configuration, and on the Audio tab you will find ACT hook options that allow you to send TTS and sound to ACT itself, instead of having Triggernometry take care of it. This will let you take advantage of ACT audio plugins, such as Discord bots that relay calls to Discord, or alternative TTS such as Yukkuri.

Do note that it's possible for individuals actions on triggers to override this hook setting. If you have gone to the configuration and allowed ACT to take care of sound output, and still find that Triggernometry is doing audio on its own, you might have triggers that are set to ignore the setting - especially if you have obtained triggers from somewhere, for example a repository, they may have this setting enabled. Your options are to either edit the actions on the triggers and remove this setting, or if you are getting the offending triggers through a repository, follow these steps:

  • Right click on the repository and select Edit
  • Locate the "Audio output setting override" option on the Setting tab
  • Set it to "Audio override setting always off"

This will disable the override on all triggers from that repository, and the triggers will respect your settings.

How do I import a trigger into Triggernometry?

Triggernometry can import both ACT triggers, and its own Triggernometry triggers. To import something into Triggernometry, simply head into the plugin's user interface, and hit the button that says "Import".

You will get a screen where you can specify what you are importing.

With the first option, you can just copypaste trigger code right there. Both ACT triggers and Triggernometry triggers are supported, so you don't have to worry about that. The second option will load triggers from a file or from an URI (a link on the web). Generally this option is meant for Triggernometry triggers, but nothing's preventing you from importing a bunch of ACT triggers through a file either. The third option is for importing any existing triggers you may already have in ACT. Note that certain features for existing ACT triggers can't be imported at this time, for example timers.

Multi-action triggers for countdowns, multiple calls with delay in between, etc

One trigger can support multiple actions, allowing you to create countdowns and multiple calls. For example, a countdown trigger for counting down the time for A6s Compressed Lightning could look something like this:

Note where it says "After (11000) ms, ..." and "After (5000) ms, ..." - that means that the action will be executed with a delay relative to the previous action (or the trigger activation, in the case of the first action). In this particular case, you would hear ACT say "10" eleven seconds after the trigger has been fired. The second call, "5" will then be delayed by five seconds from the first one. The third call, "4", one second from the first one.

The end result of all of this? A nifty little trigger which will count down 10, 5, 4, 3, 2, 1 according to the expiration time of the debuff, so you know exactly when you're needed for handling the lightning debuff. You'll hear "10" when there are 10 seconds left on the debuff, "5" when there are 5 seconds, and then from "4" to "1" every second.

Lastly, to specify the time between actions, you need to use the execution delay field on the action itself:

Variables and you

Variables are quite useful for storing information temporarily, and they can be used to manage a state, to realize logical constructs, establish conditions for triggers, or simply raid calls. You could, for example, use a variable to count down how many times a certain thing has happened, and then have ACT call out the fifth occurence of a event. When playing around with variables, you may find the variable view useful for figuring variable values out when things aren't working as expected.

In order to set a variable, you will need a variable operation action on your trigger. Triggernometry doesn't really have variable datatypes, the difference between a numeric and string expression is simply in how you want Triggernometry to interpret the expression you're feeding it. For example, a numeric expression would be evaluated as a calculation (2+2 would result in the variable value being 4), whereas a string expression will simply be used as is (2+2 would result in the variable value being 2+2).

In this example, a variable called "mysecretnumber" is being set to "${food}". What this means is that the value set to the variable will be picked up from a named group (food) specified in the regular expression - meaning, a specific part of the event text that fired the trigger.

Assuming the event text contained "2" in the relevant named capture group, that's what gets assigned to the variable. After the variable has been set, you can see it and its value in the variable view window mentioned in the beginning. You can also track the last time it was changed and what changed it, to help you with identifying issues in your variable manipulation logic.

To refer to the value of the variable, use the format ${var:variablename}. In this example, if we wanted to have ACT say the value, we could do something like below. Since the value of the variable in this example is 2, you would hear ACT call out "value is two".

You can also refer to the variable itself - or other variables - when setting its value, allowing you to make counters. With an action such as the one below, the value of the variable would be incremented every time the trigger is fired. Assuming the value started at 2, it will be 3 after the execution of this action. Next time, it will be 4, then 5, then 6, and so forth. You're not limited to increments though; you can perform all kinds of arithmetic, as Triggernometry supports a variety of mathematical operations.

With the help of trigger conditions, you can use variables to make triggers that call out a specific occurence of an event - for example if you were interested in the fourth time something happens, but you don't want calls on any preceding or subsequent occurrence.

Trigger and action conditions

Conditions can be used to limit the firing of a trigger or an action to a specific variable state, or when the event text contains a specific value (an extra check on top of the regular expression, if you will). If you, for example, wanted to fire a trigger on the fourth occurrence of an event, you might first want to set up a variable that counts down the occurrences, and then set up a condition to check for the counter value:

The construction of a condition is fairly simple - you have your left side expression (X), an operation, and your right side expression (Y). Triggernometry evaluates both expressions, performs the specified operation, and the result is either true or false. Boolean expressions of arbitrary complexity are supported through the use of groups (AND, OR, XOR, NOT). If the root group evaluates to be true, the trigger or action is allowed to fire.

Conditions can also refer to the regular expression that is about to fire the trigger; let's say you have a situation where a random player first gets a debuff, and some time later there will be an action targetting a random player, and you'd want the trigger to call it out if they're the same person. You'd first store the player's name to a variable in another trigger, and then have another trigger where the condition compares the value of the variable to the value encountered in the event text. If they match, you have your special situation where the debuff and the action targetted the same player.

Action conditions work just like trigger conditions, in that they are used to determine if an action should be executed when a trigger is firing. The rest of the actions will still be processed even though conditions may block the execution of one or more actions. If an action doesn't end up getting executed, any execution delay assigned to it will also not apply.

How can I set a specific state for variables when Triggernometry starts?

The best way to do this is to make a trigger which contains all the operations you need to set up an initial state. After you have set up the trigger, you can specify a trigger to fire on start through the plugin configuration screen. This mechanism will bypass validity checks for the trigger it fires, which means you can keep your setup trigger disabled to make sure nothing else ever fires it. You can also select a folder to fire on startup. This will fire all the first-level child triggers inside the folder, but it will not recursively go through all subfolders.

Is it possible to make a trigger fire when I'm on a specific job or jobs?

Yes - the option for this exists under folder settings, in the "Final Fantasy XIV" tab. It's possible to limit to any combat, crafting, or crafting job, and the setting applies to all triggers and subfolders inside the folder. The restriction is implicitly recursive, meaning that all subfolders will have the same restrictions in place even though they might not be explicitly defined for those subfolders. This also means that you can't ease the restriction on any subfolders, even if you would explicitly check more jobs. To enable the feature, simply check the checkbox, and then check any jobs you would like to use the triggers for.

Do you accept donations? Do you have a Patreon?

I really, really appreciate the thought, but don't worry about it - I get by. Just be excellent to each other. If you want to donate, there are better causes and charitable organizations to donate to instead, from disease research to animal shelters, etc. If you're drawing a blank, here's one idea: http://www.msf.org/en/donate

Working with party members and other entities (FFXIV)

The _ffxivparty special variable represents an array of your current party members, with some properties you can query about each one. To access a party member, you can index them with _ffxivparty[1] through _ffxivparty[8]. If you index a member out of range (not a party of eight, or you try to access the ninth member), you will simply get empty values. _ffxivparty can also be indexed by name, or by hexadecimal ID. To access a property related to a party member, the syntax is like _ffxivparty[3].name.

You will generally be the first party member, so ${_ffxivplayer} is functionally the same as doing ${_ffxivparty[1].name}. However, it's possible to define a custom job order in configuration, in which case the first player on the list might not end up being yourself. The safest way to refer to your own name remains ${_ffxivplayer}, and if you need to access yourself in the party member list, the safest way is to do ${_ffxivparty[${_ffxivplayer}].currenthp}.

Triggernometry also exposes _ffxiventity, which works similar to _ffxivparty, except you can refer to any entity in the game world. Indexing is done by name or hexadecimal ID, so for example ${_ffxiventity[Titan].x} would return the X position of Titan. If there are multiple entities with the same name, the first one seen by the plugin will be returned.

For more information on available properties and addressing, please head over to Game-specific special variables.

List variables

List variables provide an array structure for storage, and some utility additional functions. Scalar variables and list variables exist in different pools, so it's possible to have a scalar variable and a list variable with the same name existing at the same time. To refer to list variables, the expression are somewhat similar to scalar variable (Markup reference).

Visual examples of some list manipulation operations:

List variable operations:

  • Unset list variable = Removes the given list variable completely from the variable pool
  • Push value to the end of the list variable = Resolves the given expression and adds the value to the end of the given list variable
  • Insert value to the given index of the list variable = Resolves the given expression and inserts the value to the given index in the given list variable, pushing all existing items forward. If the index is higher than the list variable size, empty values will be inserted in between.
  • Set value at the given index of the list variable = Resolves the given expression and sets the value to the given index in the given list variable, overwriting any existing value. If the index is higher than the list variable size, empty values will be inserted in between.
  • Remove value at the given index of the list variable = Removes the value from the list variable, does not leave empty space behind.
  • Pop last value from list variable into a scalar variable (stack) = Removes the last value from the list variable, and places it in the given scalar variable
  • Pop first value from list variable into a scalar variable (queue) = Removes the first value from the list variable, and places it in the given scalar variable
  • Sort list in an alphabetically ascending order = Sorts the values in the list variable by using string comparison, in ascending alphabetic order.
  • Sort list in an alphabetically descending order = Sorts the values in the list variable by using string comparison, in descending alphabetic order.
  • Sort list in an ascending order based on FFXIV party job order = Tries to match the values in the list variable to player names in FFXIV party, and sorts the values based on the FFXIV job order defined in Triggernometry configuration. The setting also follows the choice regarding how the player will appear on the list. If something can't be matched to a player name, it will appear in the tail of the list.
  • Sort list in an descending order based on FFXIV party job order = Tries to match the values in the list variable to player names in FFXIV party, and sorts the values based on the FFXIV job order defined in Triggernometry configuration. The setting also follows the choice regarding how the player will appear on the list. If something can't be matched to a player name, it will appear in the head of the list.
  • Copy whole list variable to another list variable = Copies the given list variable to another list variable, overwriting and replacing its contents completely.
  • Insert list variable into another list variable at the given index = Similar to the single value insert, except this inserts the contents of a whole list variable to the given index in the given list variable, pushing all existing items forward. If the index is higher than the list variable size, empty values will be inserted in between.
  • Join all values in the list variable into a scalar variable = Concatenates all the values in the list variable into a single value, with an optional separator between each value.
  • Split a scalar variable into a list variable = Splits a value by the given separator, and adds all values into the list variable, useful for example making a list variable out of comma-separated values. Empty values will not be skipped, and will be added to the list variable.
  • Unset all list variables = Removes all list variable from the variable pool

What does (a)synchoronous mean, why are my actions being executed in a weird order?

Triggernometry's action queuing and execution is heavily asynchronous, meaning that it's processing and executing several actions at the same time, side by side. This is because it can be juggling quite a number of triggers, and all of them might be going off at the same time. However, this has the (somewhat) unwanted side effect that your actions may not always execute in the order you expect them to. Different action types take different time to execute, and even your operating system has a say in what threads might get execution priority at times. If your trigger has four sequential actions (let's call them A-D) with no delay between them, and the trigger fires, all four actions are queued to execute on the spot like this:

In most cases, this is not a problem, and you don't have to worry about this. If one of the actions changes the execution of a preceding action though, then you might be getting some funky results. For example, if action A depends on variable X, and action D is supposed to reset variable X at the end, it could be the action D actually gets executed before action A; your variable gets reset before it was supposed to! It's possible to get actions to execute sequentially though, like this:

To accomplish this, uncheck the asynchronous option on the action itself. When you do this, it means that Triggernometry will not allow the execution of other actions when that action is being executed. Actions are executed neatly one by one. Turning the option off is not recommended for everything though - if Triggernometry has to wait around a lot, then it runs the risk of falling behind. Change the execution to synchronous only for those actions where the order of execution really matters.

How can I get Triggernometry to fire my own code as trigger action?

You can add a custom callback to Triggernometry through the following:

public delegate void CustomCallbackDelegate(object o, string param);
public int RegisterNamedCallback(string name, CustomCallbackDelegate callback, object o)
public void UnregisterNamedCallback(int id)

You RegisterNamedCallback the delegate you want Triggernometry to invoke, and then on Triggernometry side you create a trigger with a named callback action, specifying the name of your registered callback. In the callback action you are also able to specify a string parameter to construct and pass to your callback. The reference to object o will also be stored and passed back to your callback when it gets invoked.

RegisterNamedCallback returns you an unique identifier you can later use with UnregisterNamedCallback, if you no longer want the callback to be invoked. Multiple callbacks can be registered with the same name, in which case they will all be invoked.

You can RegisterNamedCallback at any time, before Triggernometry has finished InitPlugin - any registrations made before InitPlugin will be applied when InitPlugin gets executed.