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.
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.
Dice literal, for example "3d6" or "d20".
Integer literal, for example "3" or "42".
Variable, such as "attack" above.
Parenthesized group, like "(3 + d6)" or "(2*3)."
Spreadsheet expression, like "[cell('Draclar', 'Score', 2)]".
Repeated group, like "3{d20+4>=20}".
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).
Multiplication and division, like "3 * 3d6", or
"attack/2".
Addition and subtraction, like "d20+4", or "d6 -
1".
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:
Attached to the macro (a local variable).
Attached to the macro group (a group variable).
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:
Variables defined in the body of the macro (from x = 3 for
example).
Variables introduced via use are also at this level.
Local variables.
Group variables.
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.
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:
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:
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.
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
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:
which will make the "Danger!" red, bold and italic.
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.
I'll break it down line by line.
The above creates a local variable, setting it to 4. Assignment statements are "identifier" "=" "whatever stuff"
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
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:
There is still a variable in this statement, so it is substituted again.
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:
As variables are lazily substituted, this just works.
Variables are case insensitive. These lines are the same:
Expressions
To understand how the system works, it is important to understand what is an expression for a roll command, in descending precedence.
When substituted, variables are implicitly grouped. This means that something like:
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:
This basically works as you would expect, but gives a couple non-obvious results.
This means that division is not distributive. It is associative.
Rounding is truncation towards 0.
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:
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:
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:
In the above example, bab and dex were explicitly accessed from the Yowa variable group. The above substitutes to:
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:
In the above example, all of the variables from Yowa will then be available and the /roll attack substitutes to:
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:
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:
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:
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:
With all that, we can finally arrive at our final example which uses all of these features:
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:
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.
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.