InnCant is a simple scripting language designed to express “favorite roll formulas” by specifying die rolls and basic operations and control structures to convert those rolls to meaningful results.
InnCant is a simple, basic subset of InnScript. It lacks the full rich object model that InnScript provides, the richer syntax, and complex runtime architecture. It is instead designed to represent “favorite rolls formulas” - roll dice, and optionally do something based on the results.
InnCant gets its name from “InnScript Cant”, where cant is “language peculiar to a specified group or profession” (with “Thieves Cant” being the most famous example). It also is a pun on the word incantation.
It is based on dice specifications - see Dice Specification
for full details of what is possible, but essentially common dice “formula” such as “3d6” will roll 3 six sided dice.
InnCant can also be extended to expose global variables and functions defined in InnScript, or other contextual information (for example, custom roll macros use fragments of InnCant, passing in the current and past die roll values as variables)
One important thing about InnCant is that it is designed to be free of side effects. The same InnCant fragment can be evaluated multiple times with no changes. This is especially true when using InnCant to roll dice - the result of one roll may determine what is rolled in the future, and so the results of the first roll shouldn’t change when the second roll happens. For example:
if 1d20 ≥ TARGET then "Hit for" & 1d8 else "Miss" end
When the d20 is rolled, it is compared against a target value, and if it equal to or higher, it rolls damage (a d8). But when we try to evaluate the damage, we don’t want
TARGET
to have changed and causing this to actually miss. By default, InnCant will prevent this. However, if you interact with external InnScript, there is a chance this might be an issue. If, for example, after being hit we increase the target value, this can cause a problem:if 1d20 ≥ TARGET then TARGET = TARGET + 5 "Hit for" & 1d8 else "Miss" end
Essentially, every time new dice are rolled, the fragment will be re-evaulated.
InnCant is roughly based around JSON in terms of the values it can work with:
For example:
%% This is a boolean
var FLAG = true
%% This is a string
var GREETING = "Hello"
%% This is an integer
var X = 10
%% This is a list (with a nested list)
var LIST = [1, "a", true, [3, 4]]
%% The first value in the list
LIST[1]
%% The last value in the list
LIST[-1]
%% This is a frame
var FRAME = {a: 10, b: 20, c: "The letter C"}
%% The value 10
FRAME.a
Note that unlike JavaScript, arrays are “1 based” (i.e, the first value is at index 1, not index 0). If a negative value is passed in, that returns the value relative to the end of the list, with -1 being the last element of the list. If the index is beyond either end of the list, the value null
is returned.
Also note that values are constant values (not references to mutable objects like JavaScript). As a result, you can not assign a new value to a list index or frame slot (you will need to create a new list/index).
Lists also support distributing values with scalar operators. For example:
var LIST = [1,2,3]
LIST + 1
Will create a new list with 1 added to every value in the list (i.e., [2,3,4]
).
Lists also support the following functions:
InnCant support a variety of operators, including common and a few less common ones:
+
, -
: Addition, subtraction×
/*
, /
, %
: Multiplication, division, modulo (remainder)&
: Concatenate∧
//\
, ∨
/\/
: minimum, maximum (which are also and/or for boolean values)==
, ≠
/!=
, <
, ≤
/<=
, >
, ≥
/>=
: Equal, not equal, less than, less than or equal to, greater than, greater than or equal toTechnically, in InnCant (and InnScript) statements are also expressions, so it is possible to include statement constructs as part of an expression - this is most useful for if
-then
-else
statements that produce a value based on the branch evaluated. So, for example:
var TEST = true
var VALUE = (if TEST then 2d6 else 1d4 end)
VALUE + 3
Will result in whatever 2d6+3
rolls.
InnCant has basic statements for simple flow control, including conditional test and looping constructs (all of which are, as mentioned above, also valid expressions). These include:
var =
: Used to set a temporary variable to a valueif then end
: If the ‘test’ expression is true, then the ‘truevalue’ expression is evaluated (otherwise the statement has the value of null
)if then else end
: If the ‘test’ expression is true, then the ‘truevalue’ expression is evaluated, otherwise the ‘falsevalue’ is evaluated.while do end
: If the test
expression is true, execute the body
expression. Repeat until the test
expression returns false. The value of the statement is the last value that body
evaluated to (or null
if it never was executed).repeat until
: Execute the body
expression. Repeat doing this until the test
expression is false. The value of the statement is the last value of body
for = [down] to [by ] do end
: Set the variable NAME
to the initial value. So long as the initial value is less than or equal to the endvalue
(or greater than or equal to it if down
is included), execute the body
expression. Each time, the value is incremented by stepvalue
(or 1 if not specified).for in do end
. Assumes that expr
is a list, we evaluate body
for each element in the list, setting the variable NAME
to that value.Created with Ignite