Code frameworks
Rulebook tables
Keep fixed rules data in a table and read it from your rules or use it as the choices of an input.
What a table is
A rulebook table is fixed data that your rules read, such as a bonus for each calling. It is not play state, and players never change it. Changing a row makes a new version of your game, like any other edit to the source.
Declare a table
A table is a JSON file in tables/. List it in the manifest.
"tables": ["tables/callings.json"]- Line 10 The paths of the table files.
{
"id": "callings",
"name": "Callings",
"columns": [
{ "key": "calling", "label": "Calling", "type": "string" },
{ "key": "bonus", "label": "Watch bonus", "type": "number" }
],
"rows": [
{ "calling": "Guard", "bonus": 30 },
{ "calling": "Scout", "bonus": 10 }
]
}- Lines 4-7 The columns. Each has a key, a label and a type: string, number or boolean.
- Lines 8-11 The rows. Each row gives a value for every column, and nothing else.
A Framework lists the ids of the tables it reads.
"tables": ["callings"]- Line 22 The Framework may read the callings table.
| Limit | Value |
|---|---|
| Columns per table | 64 |
| Rows per table | 1000 |
| Tables per Framework | 64 |
| Tables per Game | 128 |
Read a table in a rule
table.find takes the table id, the columns to match with the values to look for, and the columns to return with a default for each. It returns the columns you name, from the first row whose match columns all equal your values. When no row matches, it returns your defaults.
export function construct(
name: string,
max_hp: number,
calling: string,
): { name: string; max_hp: number; hp: number; calling: string; watch: number } {
const row = table.find("callings", { calling: calling }, { bonus: 0 });
const bonus: number = row.bonus;
return {
name: name,
max_hp: max_hp,
hp: max_hp,
calling: calling,
watch: 20 + bonus,
};
}- Line 6 Look up the bonus for the calling. The default is 0.
- Line 7 Read a column of the result. Its type comes from the table, so it needs no cast.
- Line 13 Use the bonus in a starting value.
The table id is a string written in the code, and the Framework must list the table. Every column you name must exist, and only the columns you name come back. A constructor reads a table this way. The values to match must be a variable, a field or a literal, not a computed value.
These mistakes are caught when you check the project:
const row = table.find("callings", { calling: calling }, { boost: 0 });has no column 'boost'which manifest.tables does not declare const row = table.find("callings", { calling: calling + "!" }, { bonus: 0 });table.find match values must be a variable, a field or a literalUse a table for choices
A text input can take its choices from one column of a table. The choices are the distinct values of the column, in row order.
"calling": {
"label": "Calling",
"optionsFrom": { "table": "callings", "column": "calling" },
"defaultValue": "Guard"
}- Line 11 The wizard offers the values of the calling column.
- Line 12 The default must be one of the choices.
is not one of this Framework's tablesAn action input of type text can do the same with an optionsFrom entry in the action's declaration. A source must offer at least 2 choices.

