Other Language Statements
The scripting language supports additional statements for special purposes that can be extremely useful.
A large number of target references that apply changes to objects within scripts return a value that you can ignore. For example, the "assign" target reference that assigns a tag to an object always returns the value zero. Putting it to use will normally look like the following:
var result as number result = assign[group.tag]
Declaring a variable in which to place a return value that you don't care about is unnecessary nuisance. So the scripting language provides the "perform" statement. The perform statement tells the compiler to invoke the operation that follows the statement and simply throw away the value returned. The above example changes to the code below when the "perform" statement is employed.
The net result is simpler, shorter, and clearer scripting code.
If you need to write more than a few, simple scripts, the odds are that something won't work at some point along the way. So Hero Lab includes some very helpful debugging aids. One of these aids is the "debug" statement. The debug statement allows you to have Hero Lab output any information you can access via scripts to the screen during the evaluation process. When a debug statement is executed, the string you specify is evaluated and then output to the special debug information window within HL, where you can view the results and determine what changes need to be made to your scripts. An example code block using the debug statement is provided below.
var foo as number foo = 10 var bar as string bar = "hello" debug "foo = " & foo & " and bar = [" & bar & "]"
The final output from the above script code will be as shown below within the debug info window.
foo = 10 and bar = [hello]
Using the debug statement requires that you utilize the info windows within HL. To view the debug output, go to the "Debug" menu within HL, select the "Floating Info Windows" option, and then select the "Show Debug Output" sub-option. This will display a window which contains the debug information that has been output from your scripts.
As your scripts continue executing and the debug window fills up, the oldest information will be lost off the top to keep memory consumption to a minimum.
There will be times when you need to iterate through a collection of objects, separately processing each individual object within the collection. For example, you might want to iterate through all the weapons possessed by the character to determine how many "hands" worth of weapons are equipped by the character.
In situations like this, the "foreach" statement provides the perfect solution. The foreach statement requires that you specify something to iterate over. The loop is terminated by the "nexteach" statement, resulting in the code within the foreach/nexteach block being invoked for every instance that satisfies the foreach criteria. The net result is that a typical use of a foreach/nexteach block looks like the code below.
foreach item in context where tagexpr sortas sortset ~code goes here nexteach
In the above code, the "foreach" statement stipulates what type of object is to be iterated ("item"), followed by the "in" designation and an appropriate context to search within ("context"). An optional tag expression can be specified that restricts the set of items iterated through by specifying the "where" clause and a string that contains the tag expression ("tagexpr"). The list of items can also be sorted via use of the "sortas" clause that specifies the unique id of the sort set to be used ("sortset").
There are multiple versions of the "foreach" statement that are supported. All versions work similarly, although each version iterates through a different assortment of items and context. The various versions are detailed in the sections below.
"foreach pick in container"
This form of "foreach" iterates through the picks that have been added to a container, processing only those that satisfy the specified set of criteria. With this version, the "context" must specify the container, which can be any valid context transition that identifies a container (e.g. "hero" or "child[pickid].gizmo"). The code within the "foreach" block is invoked for every pick in the container that satisfies the optional tag expression. Within the "foreach" block, the current pick being iterated can be accessed via the "eachpick." script context transition. The net result is that a typical use of this version of "foreach" looks like the code below.
foreach pick in hero where "group.tag" debug "id: " & eachpick.idstring nexteach
The above code will iterate through all picks within the hero and identify all that possess the "group.tag" tag. The "debug" statement (see below) will then output the unique id of each of the picks that are processed by the "foreach" statement, since the "eachpick." script context specifies access to the current pick within the body of the loop.
"foreach thing in component"
This form of "foreach" iterates through all things that derive from a specific component and satisfy any additional criteria. The "context" must specify the unique id of a component. The code within the "foreach" block is invoked for every thing derived from that component that also satisfies the optional tag expression. Within the "foreach" block, the current thing being iterated can be accessed via the "eachthing." script context transition. The net result is that a typical use of this version of "foreach" looks like the code below.
foreach thing in compid where "group.tag" debug "id: " & eachthing.idstring nexteach
The above code iterates through all things derived from the component with the unique id "compid" and that possess the "group.tag" tag. The "debug" statement outputs the unique id of each thing that is processed by the "foreach" statement.
"foreach bootstrap in thing"
This form of "foreach" iterates through all things that are directly bootstrapped by a specific thing and satisfy any additional criteria. The "context" must specify the thing, which can be any valid context transition that identifies a thing or a pick. The code within the "foreach" block is invoked for every thing that is directly bootstrapped by the identified thing that also satisfies the optional tag expression. This includes both bootstraps defined on the thing and on any components the thing derives from. Within the "foreach" block, the current bootstrap thing being iterated can be accessed via the "eachthing." script context transition. The net result is that a typical use of this version of "foreach" looks like the code below.
foreach bootstrap in this where "group.tag" debug "id: " & eachthing.idstring nexteach
The above code iterates through all things that are bootstrapped by the current thing/pick context ("this") and that possess the "group.tag" tag. The "debug" statement outputs the unique id of each thing that is processed by the "foreach" statement.
NOTE! This mechanism is primarily intended for use within scripts that synthesize description text for a thing.
"foreach bootstrap in entity"
"foreach actor in portfolio"
In order to facilitate debugging and to provide a convenient means for data files to report special events to the user, the "notify" statement is provided. The notify statement works similarly to the "debug" statement, except that the resulting string is reported directly to the user via a message alert. For all practical purposes, the syntax for the "notify" statement is identical to the "debug" statement, except that the line starts with "notify", as shown below.
notify "This is the message displayed"
The notification is queued and then reported to the user at the first reasonable opportunity. This means that the message is not necessarily reported to the user immediately when it is triggered. Similarly, if there are multiple notification messages, they will be accumulated by HL and then reported collectively at the next opportunity. The messages will be displayed within a single report to the user, with each message on a separate line.
The "append" statement is used exclusively with the Synthesize script. When generating dossier output, the volume of text can be quite long, in which case the traditional "@text" special symbol becomes impractical and inefficient. So the append statement is provided as a means to output a chunk of text to HL and then allow the author to stop worrying about it. Once text is output via the append statement, it will be part of the synthesized output, and it will appear in the sequence it is output.
The syntax for the append statement is simple, as it uses a single string. When an append statement is executed, the string you specify is evaluated and then output as part of the dossier. This means that three append statements in a row will procedure output that is the concatenated result of all three strings, as shown below.
append "line #1" & @newline append @boldon & "line #2" & @boldoff & @newline append "line #3" & @newline
The final output that results from the above three statements would look like the following when synthesized for HTML:
line #1<br> <b>line #2</b><br> line #3<br>
As a general rule, scripts should not be directly modifying the contents of "user" fields. Those fields are intended for access only by the user, so attempts to modify them via scripts are inherently considered an error by the compiler. However, as with any rule, there are always the exceptions.
When a script needs to modify the value of a user field, the "trustme" statement can be used. This tells the compiler that you know what you're doing and can be trusted to not do things inappropriately. Once specified within a script, the script becomes trusted and the compiler stops checking for code that modifies user fields.
The syntax of the trustme statement is trivial. Simply place the statement on its own line within the script. There are no parameters, so it looks like below.
IMPORTANT! Whenever you think that you need to utilize the trustme statement, look closely at your script and what you're trying to do. It is rare that you should actually need to use trustme, and your data files probably can be better structured to eliminate the need for directly modifying a user field.
IMPORTANT! The following script types are automatically designated as "trusted". This is because their general nature is such that you'll often be needing to modify user fields.