Expanded Macro Processor

The expanded functionality of the chat processor can now insert variables into your roll expressions. You can also do basic conditional statements with if.

Chat Commands

As a refresher, the server checks if a line of input from a chat message is a command. If it is not a command, it is just text and is just echoed to all users. If it is a command, then something special happens. The server considers a collection of lines all at once.

The current commands are:

Roll

roll, r, /roll, /r or any line of text that can be successfully parsed as a dice expression.

The server parses the command as a dice expression, rolls the dice and displays the result.

Shuffle

shuffle, shuf, shuff, /shuffle, /shuff, /shuffle stuff

The text after the shuffle is split on whitespace (although quoted text is treated as a single thing) and then shuffled. The result is then sent back. This is useful for if you need a random order of some things.

Choose

choose, /choose stuff

The text after the choose is broken up the same way as with the shuffle command. One of them is chosen and sent back.

Table

table, /table tablename

The named table is looked up and rolled on (which can trigger a roll on a different table). The result is then sent back. See tables.

Name

/name some name

Your chatname is temporarily changed to the given name for display purposes in this collection of lines.

Chat

Any general text.

Not really a command, but here for completeness's sake. Just send some text.

Say

/say some text

Says the text, without trying to parse it as a die roll.

Style

  • /color red
  • /bold
  • /italic
  • /underline
  • /big
  • /boxed
  • /anon
  • /reset

Applies a style to the rest of the chat commands in the current macro/multiline input. Color can be a valid CSS color. bold, italic, underline, big, boxed and anon are all toggles. Issuing them a second time will turn them off. Reset will clear all currently applied styles.

Unlike other commands, multiple styles can be applied at once. For example:

/name Jon
/color red bold italic
Danger!
/reset
This is plain.

which will make the "Danger!" red, bold and italic.

Jon: Danger!
Jon: This is plain.

Macro Processor

Now for the extra functionality. Basically, a preprocessing step interprets the input, altering the lines that are sent to the server.

Example

Here's an example, we will then explain afterwards.

bonus = 4
attack = d20 + bonus
/roll attack

I'll break it down line by line.

bonus = 4

The above creates a local variable, setting it to 4. Assignment statements are "identifier" "=" "whatever stuff"

attack = d20 + bonus

The above also creates a local variable, named attack. This one indirectly references the local variable we created before. When it comes time to use this variable, multiple expansions will need to happen. I'll explain that in a second

/roll attack

The above is a roll statement, so we try to replace any terms we can with known variables. After the first substitution, we end up with:

/roll d20 + bonus

There is still a variable in this statement, so it is substituted again.

/roll d20 + 4

which is the final line. This line is what will end up being sent to the server.

Variables only have to be defined when you get to actually substituting them. This means that we could have written the above as:

attack = d20 + bonus
bonus = 4
/roll attack

As variables are lazily substituted, this just works.

Variables are case insensitive. These lines are the same:

/roll attack
/roll ATTACK
/roll Attack

Expressions

To understand how the system works, it is important to understand what is an expression for a roll command, in descending precedence.

  1. Dice literal, for example "3d6" or "d20".
  2. Integer literal, for example "3" or "42".
  3. Variable, such as "attack" above.
  4. Parenthesized group, like "(3 + d6)" or "(2*3)."
  5. Spreadsheet expression, like "[cell('Draclar', 'Score', 2)]".
  6. Repeated group, like "3{d20+4>=20}".
  7. Unary operations, like "!4" or "-d6". The unary operations are "!" (not, turns 0 to 1 and all other values to 0), "-" (negative), and "+" (which does nothing).
  8. Multiplication and division, like "3 * 3d6", or "attack/2".
  9. Addition and subtraction, like "d20+4", or "d6 - 1".
  10. Comparisons, like "d6 == 1" or "d10 <= 2". The comparison operatiors are "=", "<", "<=", ">", ">=", "!=".

When substituted, variables are implicitly grouped. This means that something like:

x = 3 + 2
/roll 3*x

will evaluate to 15, as you would expect.

Integer literals are always positive, but since unary operations are high enough precedence, "-4 + 2" works in the normal way, producing -2.

Parenthesized vs Repeated groups

A parenthesized group can be anywhere in an expression and is just for changing precedence. A repeated roll must always start with an integer and is for evaluating the bracketed group multiple time, summing their results. Brackets were chosen to avoid conflating it with multiplication like you would get with parentheses.

Rounding

As per the usual rules of rpgs, if you are left with a fractional result, that result is rounded down. That is still ambiguous, so to be explicit, rounding will occur at these times:

  • Before adding, subtracting, or comparing two values.
  • The final total will be rounded.

This basically works as you would expect, but gives a couple non-obvious results.

# This produces 2
/roll 2/3*3


# This produces 0, left side is
# rounded down to 1, which is not
# greater than 1
/roll 3/2 > 1


# This produces 2, as the left side of
# the plus and the right side of the
# plus are both rounded to 1.
/roll 3/2 + 3/2

This means that division is not distributive. It is associative.

Rounding is truncation towards 0.

/roll -3/2 # produces -1

Grouped Variables

It's annoying to have to define all your variables in every script, especially when they are values you'll want to use in many places, like your strength bonus or your base attack bonus. To help with this, you can define variables in 3 places outside of a macro's body:

  1. Attached to the macro (a local variable).
  2. Attached to the macro group (a group variable).
  3. In the special "globals" group (a global variable).

Each of these locations can have any number of variables.

A local variable is always visible in the macro, but cannot be accessed from other macros.

A group variable is visible to macros in that group, but can also be accessed from other groups via a "dotted" identifier or by a use statement.

A global variable is visible to all macros.

If these variables conflict, the first in the list is used:

  1. Variables defined in the body of the macro (from x = 3 for example).
    • Variables introduced via use are also at this level.
  2. Local variables.
  3. Group variables.
  4. Global variables.

Not only are these variables accessible in a macro, they are also available in the chat box. You can select which group is active by clicking on its tab above the chat. The command from the chat box is then treated as if it were a macro in that group for the purposed of variable visibility.

Dotted Identifier and Use

Imagine you have a variable group named Yowa which has bab as 3 and dex as 2c.

An example of a dotted identifier is as follows:

attack = d20 + Yowa.bab + Yowa.dex
/roll attack

In the above example, bab and dex were explicitly accessed from the Yowa variable group. The above substitutes to:

/roll d20 + 3 +2

This can get pretty repetitive, so to bring all of the variables from a variable group into the local context, you can use the use statement.

You can then do this:

use Yowa
attack = d20 + bab + dex
/roll attack

In the above example, all of the variables from Yowa will then be available and the /roll attack substitutes to:

/roll d20 + 3 + 2

Variable groups are also case insensitive.

Variable Kinds

There are four kinds of variables that can be defined.

Number Variables

These are pretty straightforward, they are negative or positive integers.

Boolean Variables

These variables can only take the value of true or false, which are treated as 1 and 0 respectively.

Expression Variables

These variables are stored as strings, but they must succesfully parse as an expression in order to be used. You can still take advantage of lazy evaluation of variables. For example, "d20 + attack" is a legal expression, but "d20 +" is not as there is no postfix "+" operator and so it is an incomplete add expression, missing the right hand side.

Expression variables are very powerful due to the lazy evaluation. Globally defined expression variables can thus refer to locally defined variables in your macros.

You can think of these variables as formulas.

Ability Score Variables

These are the same as number variables, but their actual value is the 3.5 ability score modifier that number would produce. For example, a 17 would be treated as a 3 in macros.

Spreadsheet Expression

You can embed references to spreadsheet cells using square brackets. Additionally, bare identifiers will be evaluated as a spreadsheet expression in the context of the spreadsheet that is the same name as the active macro tab. This is useful with named cells. So you can have something like this:

d20 + ref

And if you have a spreadsheet named after your macro group (like named after your character) and on that spreadsheet is a cell named "ref" with the value of 2, then the above will be expanded into d20 + 2

Repeat Statements

You might need to roll certain dice multiple times and don't want to just copy paste the command (as if you want to change it you'd have to edit each line). To facilitate that, there is a repeat statement that will execute the contents of it a certain number of times. It looks like this:

repeat 4:
  d20 + 3

Which will roll d20 + 3 four times, as four different statements.

The 4 in the above example can also be a variable or an expression. It cannot contain a dice expression and must evaluate to a number. If it evaluates to a negative number or zero, the contents of the repeat statement are executed zero times.

repeat can alternatively be spelled as loop.

If Statements

The last major feature is conditional execution. This is done using if and else. An example:

rapid = 1
if rapid:
  /roll d20 - 2
  /roll d20 - 2
else:
  /roll d20

The if statement is written as "if" "expression" ":". The trailing colon is mandatory. Without it, it is considered a regular line of text.

An expression evaluator is then run and if the result is non-zero, it is considered "true". If it's true, then the indented statements after the if are evaluated.

An if can have an else, which looks like in the above example. The indented statements after the else are run if the expression turned out to be false (0). An if doesn't have to have an else. There's also else if.

With the if you can conditionally send roll commands and also conditionally set variables. For example:

prone = 1
if prone:
  penalty = 4
else:
  penalty = 0
/roll d20 - penalty

With all that, we can finally arrive at our final example which uses all of these features:

/name Yowa
use Yowa
attack = d20 + dex + bab
if rapid:
  Rapid Attack!
  /roll attack - 2
  /roll attack - 2
else:
  /roll attack

No Dice in Ifs

Note! if conditions cannot contain dice expressions, directly or indirectly.

Escaping

Rarely, you want to type something that looks like a roll expression, but isn't, such as telling someone how to roll something:

To roll a strength check, type:
Roll d20 + str

Variable substitution and expression evaluation will be attempted on this, which would be confusing as it would come out as "Roll d20 + 3" if you had a str variable set to 3, which would then get rolled! To avoid this, you can prefix with a "/say " which will cause the rest to be processed as literal text. You can also use a leading backtick (`) to the same effect. Do not use a trailing backtick.

/say Roll d20 + str
`Roll d20 + str

Generally, slash commands other than /roll are not substituted, so if future slash commands are added there will be no problem.

The roll command has to succesfully parse as a dice expression in order for substitution to get involved, so this is a pretty rare occurrence.