LavishScript:Triggers

From Lavish Software Wiki
Revision as of 16:44, 28 December 2010 by Lax (talk | contribs) (→‎Processing a script command queue)
(diff) ←Older revision | view current revision (diff) | Newer revision→ (diff)
Jump to navigation Jump to search

Overview

LavishScript uses Blech, an open-source solution for picking needles out of a haystack, in order to quickly and easily provide a method of text-based "triggers". A trigger can cause an action to be immediately executed or placed in a script's command queue, upon receiving a line of text that matches the trigger text.

For example, if a game produced a line of chat that says

You have entered the kitchen.

and if that text was filtered through the LavishScript trigger handler (by a LavishScript module, or in the case of Inner Space, a game-specific IS extension), a LavishScript trigger could execute a function when a line matching this general form is found, and even retrieve the variable portions (e.g. "the kitchen") for you.

Note that this does not state nor imply that any given game's text will be filtered through the trigger handler -- it must be explicitly done per game. Any text that flows through a LavishScript console, however, is automatically filtered through the trigger handler.

Trigger Text format

Trigger text must match the ENTIRE line, not just a portion, in order to "hit". This makes for common mistakes of explicitly allowing unknown portions of text before and after the actual trigger text -- avoid doing so at all costs, as this is not only inefficient, it can also be costly in other ways if someone wants to be malicious. LavishScript trigger text is case insensiive.

Blech allows for two types of variable portions of any given trigger text, which essentially boils down to known, and unknown. Known variable portions would be things known during script execution, but not at the time the script developed, and that may change during the lifespan of the trigger. If the variable portion would not change during the lifespan of the trigger, then it is in fact not variable at all, and would simply be text used as part of the trigger. Unknown variable portions would be things that simply cannot be determined without having direct access to the game logic -- an amount of damage being done in a hit, the name of a monster, and so on. LavishScript uses the @ symbol for unknown, and % symbol for known. To use these characters as part of the trigger text instead of an indication of a variable portion of the text, simply use two of the character consecutively, such as @@ or %%, to mean one instance of that character.

To indicate an unknown variable portion, create a name and place the name between two @ symbols, such as @damage@. LavishScript ignores the name, so just make it something to do with what the variable portion of the line would contain (note that the same name can be used multiple times in the same line). The only name that has any special effect is *, such as @*@ -- more on that later.

To indicate a known variable portion, place text and escaped data sequences between two % symbols, such as %\${Target}'s corpse%. Escaping of course ensures that the data sequence is reduced to text when the trigger is checked, as opposed to when the trigger is created.

Creating a Trigger in LavishScript

Triggers can be created in a script using the AddTrigger command. This command requires two parameters, one being the name of a function for when the trigger hits, and the other being the trigger text. If the function is atomic, the trigger will execute the atom immediately. Otherwise, a function Call is placed in the script's command queue. Any number of triggers may use the same function or trigger text. If multiple triggers hit on the same text, the functions will execute in arbitrary (unspecified) order. Some order CAN be enforced by using atoms and functions separately for the same trigger text -- the atoms will be executed immediately, while the functions will rely on the script to explicitly process its command queue.

/* You have entered the kitchen. */
AddTrigger GotRoom "You have entered @room@."
/* There is a chef standing here. */
AddTrigger GotNPC "There is @who@ standing here."

All trigger functions, atomic or not, must follow the same basic declaration. First of all, there is always at least one parameter given to the function, and that is the full line that matched the trigger text. So we start with this:

function MyTriggerFunction(string Line)

Next, each unknown variable portion is passed as a parameter, in order from left to right, unless the name * was used in the trigger text (that @*@ mentioned above). For the first line in the above example, that would be "room", and for the second, that would be "who". Remember that the names from the trigger text make absolutely no difference except for *, so we do not need to use the same names in our function.

function GotRoom(string Line, string Room)
function GotNPC(string Line, string NPC)

In this case, both of the values are text, so we use a string to receive the parameter. There are plenty of cases where this is not preferable, such as when dealing with numbers:

/* You hit a chef for 42 points of damage! */
/* You hit a butcher for 1 point of damage! */
AddTrigger YouHit "You hit @who@ for @damage@ point@*@ of damage!"
/* The point@*@ will hit both "point" and "points", ignoring the s, and covers both "You hit" messages. */
atom YouHit(string Line, string Who, int Damage)

In this case, the atom will receive two unknown variables.

Processing a script command queue

If non-atomic functions are to be used for triggers, the script's command queue must be explicitly processed. This ensures that the flow of the script is not disrupted.

The following loop will process the entire command queue.

while ${QueuedCommands}
  ExecuteQueued

Make sure to process the queue wherever it may be critical within the script.

It is also possible to process only portions of the queue containing a given substring. Since a Call is placed in the queue, essentially the same as

QueueCommand Call GotRoom "You have entered the kitchen." "the kitchen"

or

QueueCommand Call GotNPC "There is a chef standing here." "a chef"

... you may wish to execute any of the room triggers, like so:

while ${QueuedCommands[Call GotRoom]}
  ExecuteQueued "Call GotRoom"

Tips for using triggers

  • Blech is highly optimized and extremely simple to use, so use it wisely to retrieve the unknown portions of the text for you if at all possible. Most games use very simple text output, so it's generally unnecessary to use regular expressions (though you can use the Regex module) or break matched lines down yourself. If you can possibly get away with it, have Blech do ALL of the parsing for you. There ARE some cases where it is useful to perform your own processing on the line or portions of the retrieved variables.
  • Avoid starting with unknown variable portions if possible. Blech will be most efficient when the trigger text has a known first portion. There are plenty of cases where the first part really is unknown, and this is okay -- Soandso says, "Go ahead, make my day" -- but especially avoid using variables where there are none. For example "Welcome to Such and Such Game!". This text is fully known in advance, and should NOT be tainted by a trigger text like "@*@Welcome to Such and Such Game!@*@".
  • Sometimes, a WaitFor command would be more appropriate to use than a trigger. WaitFor is very useful for waiting for one of a handful of responses to a given action, or a single known response to a given action. WaitFor is a case insensitive substring search, which is applied to all trigger checks, after triggers themselves (an important distinction, particularly if you want to wait until the moment a given atomic trigger has executed).

See Also