XCore Custom Roll API

The XCore ruleset is a generic table top that includes a number of core rolls that allow you to generate appropriate dice rolls for most mechanics. However the gaming experience can be improved by creating rolls with additional automation. 

We have built an API to make it much easier to write and add your own rolls. You can download a sample extension from the Fantasy Grounds Forge here: https://forge.fantasygrounds.com/shop/items/1541/view 

This extension currently contains 3 files which will be outlined below. Experienced Fantasy Grounds coders will probably be able to get everything they need from the files. For newer people I will walk thru the files below.

File Overview

I always write my extensions in the [1] Fantasy Grounds data folder, under extensions and then a [2] new folder name that represents the extension name/purpose.

You should understand that Fantasy grounds will always use this version first, over any packed extensions or any extensions in the vault.

The most important file here is the [3] extension.xml file – this is the only required file for an extension – however it is best practice to break other functions out into additional files.

I have created a [4] \scripts subfolder for the rolls.

I have compressed/packed [5] extension file but it is not used by Fantasy Grounds in its current location. I keep it here so Fantasy Grounds does not use it, and I need it to upload to the Forge or to otherwise share it.

extension.xml

This is the most essential file of any extension. Without this file the extension will not be seen by Fantasy Grounds.

You can specify image resources in a separate graphics file. As I have only one image – an [1] Icon for the announcement I have included the single line entry in this file. 

Section [2] contains meta data for the Fantasy Grounds engine. I typically match the Name and Description. Look at other extensions in your Fantasy Ground client to understand how you should name this. 

Section [3] contains the announcement text. you need to use escape characters to add line breaks. Note that the announcement text will not update on a /reload command – you need to exit Fantasy Grounds to see changes to this section.

Section [4] loads the script files that contain our rolls. The script names need to be unique – not just to this file but across the ruleset and any other loaded extensions.

Roll Example 1

Our first example is the PbtA roll script that adds support for the Powered by the Apocalypse roll mechanic. This is basically 2d6+modifiers and results are compared to 6-, 7-9, 10 and 12+. This is a simple example in that the roll itself does not make any changes to the character or target (like a damage roll would).

Function [1] must be renamed but only rename the highlighted section. You will reuse this highlighted section in other parts of the script also. This must be unique. Do not change the handlePreprocess part of the name. rollInfo is data that this function receives from the API. A Debug.chat(rollInfo); will show you that data.

The string.sub [2] is extracting the dice command from the roll string that is passed to this script. The full string might contain pbta 2d6+1  and we need to extra the 2d6+1 We do this by grabbing the data starting after the 5th character [5] and going to the last character [-1]. This is returned to the API as sRollString in the final line.

The highlighted [3] section is what we use in the actual rolls – this pbta syntax will tell XCore to use this roll script. This must be unique across XCore and all your loaded extensions.

Function [4] must be renamed the same way we renamed Function [1]. Do not change any other part of this line. rSourcerTargetrRollrMessage is data that this function receives from the API. A Debug.chat(rSourcerTargetrRollrMessage); will show you that data.

Line [5] will total the dice and modifiers that have been rolled. We are not using any targets in this roll.

Section [6] compares the dice total to various levels of success or failure and appends the result text to the existing rMessage data

This function loads the roll into the API so that it is available for use.

[7] is the string used in the rolls – same as in [3]
[8] is the preprocess function from [1]
[9] is the handle function from [4]
[10] is the icon displayed on the value/roll field to provide a visual clue as to what data/roll is stored in the field. See below for all the included icons. You can add your own 12×12 pixel icons if you cannot find something suitable. See the icon example in the extension.xml file at top of this screen.

Roll Example 2

Our second example is the openlegend roll script that adds support for the Open Legend RPG core mechanic.

There are several parts to this roll. Your rank in the skill determines how many and what size die you will roll, dice can explode and you can add extra dice for Advantage or Disadvantage. There is also different degrees of challenge difficulty.

In function [1] we append OL after the handlePreprocess – we will use that again in the handle script We use string.sub [2] to extract the Skill Rank and save as drs We also store the same value in sValue but this time we get the data from rollInfo.sValue

We get the [3] Advantage and Disadvantage values from the character sheet by extracting the roll id from rollInfo.nodeRoll and we store those values in new variables. These come from fields (b) and (c) on the roll (see [6] and [7]).

We start the next section by checking if we have the same number of Adv and Disadv [8]. This may be that we have none of either (most likely) or that we have a situation granting us the same amount of each. We then use the Rank stored as sValue to set sDiceSet with a new dice string [9][10]. All rolls have at least a d20. If you have a Rank value above 0 then you will also get one or more d4, d6, d8 or d10 dice. All dice can explode on the maximum value – this is set by using the ! after the dice.

We continue by checking if we have more Adv than Disadv [11]. We then use the Rank stored as sValue to set sDiceSet with a new dice string. If the Rank is 0 then we add a second d20 and we only keep the highest d20 [12]. If you have a Rank value above 0 then you will get one or more d4, d6, d8 or d10 dice and Adv will grant you an additional die of that type. All dice can explode on the maximum value – this is set by using the ! after the dice. We then drop the lowest value dice equal to the number of dice we added for Adv [13].

We continue by checking if we have more Disadv than Adv [14]. One of these conditions is always true – we have an equal number of Adv and Disadv or we have more Adv or we have more Disadv. We then use the Rank stored as sValue to set sDiceSet with a new dice string. If the Rank is 0 then we add a second d20 and we only keep the lowest d20 [15]. If you have a Rank value above 0 then you will get one or more d4, d6, d8 or d10 dice and Disadv will grant you an additional die of that type. All dice can explode on the maximum value – this is set by using the ! after the dice. We then drop the highest value dice equal to the number of dice we added for Adv [16].

That concludes the else/elseif loop.

We then reset the Adv and Disadv values on the character sheet [17] as these would typically only be valid for one roll. 

We then set the string openlegend to be associated with this roll [18].

We then tell the API to roll our new dice string in [19].

That concludes the handlePreprocess function.

Function [20] must be renamed the same way we renamed Function [1]. Do not change any other part of this line. rSource, rTarget, rRoll, rMessage is data that this function receives from the API. A Debug.chat(rSource, rTarget, rRoll, rMessage); will show you that data.

[21] will total the dice and modifiers that have been rolled. We are not using any targets in this roll.

Section [22] compares the dice total to various levels of success or failure and appends the result text to the existing rMessage data. We dont know what the Challenge target is so we list the highest level of success that we achieved – anything below that is also a success. If you rolled 18 it would say [15+ Challenging] – if the difficulty was Challenging or lower you would be successful. If the difficulty was higher than Challenging some complication is introduced.

This function loads the roll into the API so that it is available for use.

[23] is the string used in the rolls – same as in [18]
[24] is the preprocess function from [1]
[25] is the handle function from [20]
[26] is the icon displayed on the value/roll field to provide a visual clue as to what data/roll is stored in the field. See below for all the included icons. You can add your own 12×12 pixel icons if you cannot find something suitable. See the icon example in the extension.xml file at top of this screen.