Index of pages
Introduction
Resources
Windows and Controls
The Data Base
Scripting
Templates
Extensions



Scripts are sets of instructions forming sequences of commands that can be used to implement program logic in rulesets. Typical uses for scripts are calculations, operation based on a condition and context specific custom handling of user input.

About Lua

Lua 5.1 is the scripting language used by Fantasy Grounds. It is a simple procedural language with powerful data description constructs. The name of the language is spelled "Lua" (not "LUA").

Some useful links for learning the Lua language and working with scripts are listed below.

Global Lua variables, functions and packages

Not all built-in Lua functions and standard libraries are available in the Fantasy Grounds scripting environment. The following lists the variables and functions that are NOT available.

  • _G
  • dofile
  • getfenv
  • getmetatable
  • load
  • loadfile
  • rawequal
  • rawget
  • rawset
  • setfenv
  • setmetatable

The following standard libraries are available:

  • math
  • string
  • table

In addition, the following functions have had their functionality slightly altered.

  • The print function prints its input into the console. To access the console, use the "/console" command, or see the console.log file in the application data folder.
  • The type function has been extended to cover some custom data object types.

Registries

There are two registries available in the scripting environment. Registries are persistent tables of data that are stored when the session is closed, and restored when it is restarted. All the data in the registries is specific to the local installation and is not delivered over the network to connected users. These facts make the registries ideal for storing user preference data, and data related to the state of script constructs.

Registries may contain tables, numbers, strings and booleans. Other data types will not be preserved, and using them might lead to unpredictable behavior.

The global registry

The table GlobalRegistry is shared by all rulesets and campaigns used with the installation on a single computer.

Warning
To avoid interference with other rulesets, always attempt to include a subtable identifying the ruleset or context the values in the global registry are stored for. A convenient method is to create a table identified with the key obtained from User.getRulesetName and use that table for the actual data.

The campaign registry

The table CampaignRegistry is specific to a campaign, and is not loaded or visible from other campaigns.

The campaign registry is the preferred position for any data that links to or operates on data from the campaign database. It is also entirely specific to the ruleset used in the campaign.

Using scripts

Script blocks defined in rulesets can be contained in three locations: window classes, controls and global script packages.

In all cases, script blocks are defined using <script> tags. The body of the script block can be specified as the text contents of the tag, or in an external file referred to using the "file" attribute. The following exmple illustrates the latter.

<script file="scripts/pointerselection.lua" />

If the script is given as the text content of the tag, the entire block is processed as a single line due to XML processing details. In this case, the following restrictions apply.

  • Any error messages printed on the console will always point to line 1, generally making them less useful
  • The "--[[ ]]" comment syntax must be used instead of the "--" syntax
  • XML special characters such as "<" and ">" must be escaped not to interfere with XML processing

Script block scope

The global scope in the script environment is not available to user defined scripts. Instead, each script block receives its own environment and the entire block is evaluated and executed treating that environment as global.

This means that all functions and variables defined in a script block can be used inside the block as if they were global, and all other script blocks are incapable of directly modifying another block's variables.

To access other script blocks, such as other controls in the same window, many script block environments contain special variables that can be used to access related environments. These are detailed for each element separately in the reference documentation, but the most common are summarized below.

  • A window script environment has a variable named the same as a control for each of its controls, pointing to the environment of the control script block
  • A control has a variable called window pointing to the environment of the window's environment
  • Windows in a windowlist have a variable called windowlist pointing to the window list control's environment
  • A window instance in a subwindow control has a subwindow variable pointing to the environment of the sub window control
  • The variables super and self are available in environments inheriting or being inherited by other environments. See the templates section for more information.
  • Script packages are globally available by their name (see below)

Script blocks in window classes

Script blocks in window classes are applied to each window instance created based on the class, and extend the windowinstance interface.

The script tag should be located as a direct child of the <windowclass> tag, as illustrated by the following abbreviated example.

<windowclass name="charsheet_skilllistitem">
...
<script file="scripts/charsheet_skilllistitem.lua" />
<sheetdata>
...
</sheetdata>
</windowclass>

Script blocks in controls

Script blocks for individual controls are applied to the control in each window instance based on the containing window class. The control scripts extend the interface of the control's type.

The <script> tag should be a direct child of the control definition tag.

<numberfield name="...">
...
<script>
function onValueChanged()
if getValue() &lt; 0 then
setValue(0);
end
end
</script>
</numberfield>

Script packages

Script packages are globally accessible generic script constructs similar to Lua standard library packages. Their operation is detailed on the reference page script.

They can be accessed from other script blocks by name, which must be defined as the "name" attribute to the <script> tag. Specifying the name is not mandatory - if it is omitted, the contents of the script can still be executed using the onInit function.

<script name="ModifierStack" file="scripts/modifierstack.lua" />

Accessing XML parameters from script

It is possible to access the XML tags given in the window class or control definition from inside respective script blocks. This is useful for the separation of script logic from the configuration parameters used, such as icon names or data base field names used.

Each tag under the control definition is presented as a table value with the same name. The child tags are each represented in the table with integer indices. Nested tags with children are included as nested tables. Any tag with no child tags is represented as a string value if it has text contents, or the boolean value true if the containing tag is singular or empty.

The following definition is provided as an example.

<genericcontrol>
<states>
<empty>
<icon>emptyicon</icon>
</empty>
<normal>
<icon>normalicon</icon>
</normal>
</states>
<flags>
<first />
<second />
</flags>
<label>Control label</label>
</genericcontrol>

The table value in the script environment could be written as:

states = {
[1] = {
empty = {
[1] = {
icon = {
[1] = "emptyicon"
}
}
},
normal = {
[1] = {
icon = {
[1] = "normalicon"
}
}
}
}
}
flags = {
[1] = {
first = {
[1] = true;
}
},
[2] = {
second = {
[1] = true;
}
}
}
label = {
[1] = "Control label"
}

Using the ruleset reference document

The ruleset reference documentation details the interfaces used for scripting, for the various objects available. Packages and objects are interfaces available only via scripting. Many elements contain functions usable by scripts. These are detailed in the "Interface" sections on the reference document pages.

Event functions

Many elements contain event functions, indicated as such with the "event" denomination in the reference document. These functions are automatically called by the system when certain events occur.

To take advantage of events, a script block needs to be created extending the element defining the event function. A function named according to the event name should be defined. If the function is present, it is called when the conditions for the event are fulfilled. No further actions need to be taken for the event to work.

The example in the "Script blocks in controls" section above contains an example of an event definition.

Handler functions

Some script interfaces define handler functions, identified by the denomination "handler" in the reference documentation. A handler is different from an event in three regards. First, any number of scripts can receive a single event fired on a handler. Second, a handler requires explicit registration to operate on a user defined function. Third, the user defined handler function does not need to be located in the script block extending an object defining a handler function, but can be in any script environment.

To register handler events on a user defined function, use the assignment operator on a handler as if it were a function variable. The following example defines a handler function (onSourceUpdate) and sets the onUpdate handler of a data base node to point to that function.

-- Create a handler function
function onSourceUpdate(source)
setValue(calculateSources());
end

function addSource(name)
local node = window.getDatabaseNode().createChild(name, "number");
if node then
sources[name] = node;
-- Assign the handler
node.onUpdate = sourceUpdate;
hasSources = true;
end
end

Technically, the assignment performed is not a regular assignment operation. A handler can receive one handler function for each script block environment. Therefore, a single data base node could be monitored by a number of different individual functions, e.g. in different controls.

The assignment is done based on the environment of the function being assigned as the handler. This leads to a very nice and simple way of using handlers in most cases. The case that requires special attention is one in which the handler needs to be reset, i.e. removed. Such situations must be handled by assigning another function to receive the handler events. Typically, this function is one that does nothing. The simplest way to accomplish this is to pass in an inline function.

node.onUpdate = function() end;

'Fantasy Grounds' is a trademark of SmiteWorks Ltd. All other trademarks are the property of their respective owners.
2004-2010 SmiteWorks Ltd. ALL RIGHTS RESERVED.
Privacy policy