Rule systems
The dice language
The formulas behind every sentence: dice notation, values a roll can read, how a roll is read, with recipes for common mechanics.
One small language under every sentence
Every number in a rule system is either a plain number or a formula in one small language: the dice a check rolls, a target, a track's maximum, a stat's modifier, an enemy's health by tier, an amount on a rule card. You rarely need to write one: the sentence editor, the composer and Hob write them for you. The Advanced tab shows them all when you want to.
It is deliberately small. It is not a programming language: no loops, no reaching outside the values it's handed, nothing that can misbehave. A formula that doesn't make sense is refused when you save, in a sentence saying what's wrong.


Dice
| Write | Means | Example |
|---|---|---|
d20 | One twenty-sided die | 13 |
3d6 | Three six-sided dice, added | 4 + 2 + 6 = 12 |
2d20kh1 | Two d20s, keep the highest one (advantage) | (7, 16) → 16 |
2d20kl1 | Keep the lowest one (disadvantage) | (19, 7) → 7 |
4d6kh3 | Four d6s, keep the best three | (5, 2, 6, 3) → 14 |
(@stat)d6 | As many dice as a value says | Prowl 3 → 3d6 |
d(@dexterity) | A die as big as a value says, for games where a stat is a die | Dexterity 10 → d10 |
6d10>=6 | Count the dice at or over 6: the value is the count | (2, 6, 9, 10, 1, 7) → 4 |
3d6! | Exploding: a top face rolls another die | (6, 2, 4) + 5 → 17 |
4dF | Fate dice: each −1, 0 or +1 | (+, 0, −, +) → +1 |
d100 | Percentile; d% is the same | 47 |
d100t(1) | Percentile with a bonus tens die (keep the better tens); t(-1) a penalty die | (4 or 7, 3) → 43 |
d12[Hope] | A die with a name: shown with its name, and readable by rule cards | Hope d12 (9) |
Every face is kept in the roll's record, so the dice tray can replay it and the roll chip can show it. All dice roll from a seed: the same roll, rolled again for the same turn, gives the same faces.
Numbers and logic
- Arithmetic
+ - * /and brackets:floor((@str - 10) / 2)- Comparisons
> >= < <= == !=, giving 1 or 0:@hp < 5- Logic
and,or,not:@cond.blessed > 0 and @nerve >= 3- Choices
if … then … else …:if @level >= 5 then 2 else 1. Only the branch taken is worked out, so only its dice are rolled.- Functions
floor ceil round abs min max clamp:clamp(@edge + @cond.edge, -1, 1)
Values a formula can read
| Write | What it is | Where |
|---|---|---|
@str | A stat's value (by its id) | Anywhere |
@str.mod | A stat's modifier: its value when it has none | Anywhere |
@stat | The stat being rolled; @stat.mod its modifier | A check's roll and target |
@hp | A track's current value; @hp.max its maximum | Anywhere |
@proficiency | A derived value (by its id) | Anywhere |
@level | The character's level; @xp their xp. In an ability set to Scales with the level in a class, its own uses, cost, effects and modifiers read @level as their level in that class, while they hold it | Anywhere |
@class.hit_die | What their class says for a value (the system's default when it says nothing, worked out as that class's own; else 0). With several classes, each class's adds up: added together, the highest or their first class's, as the system sets | Anywhere |
@classlevel.wizard | Their level in a class (by its id), 0 when they don't hold it | Anywhere |
@classlevel | Their level in this class: 2 * @classlevel grows with each level in it | A class's own values, and the system's class defaults |
@classfirst | 1 for the class they took first, 0 for any other: what only the first class gets | A class's own values, and the system's class defaults |
@own.hit_die | This class's own value of another name (its hit die), where @class.hit_die would read the added-up one | A class's own values, and the system's class defaults |
@classes | How many classes they hold; @mastered how many are at their cap | Anywhere |
@rank | This ability's rank: 1 when first taken, one more each time it is taken again | An ability's own cost, effects, uses and modifiers |
@rank.toughened | An ability's rank (by its id), 0 when they don't know it | Anywhere |
@difficulty | The number behind the word the storyteller picked for a label (Hard → 20). Every label is readable by its id: @edge, @position. | A check's roll and target |
@spend_push | 1 if the player paid that spend before the roll, else 0 | A check's roll |
@cond.edge | What the sheet's conditions add up to as edge; @cond.mod as a bonus or penalty | A check's roll |
@cond.poisoned | A condition's level (0 when absent); @cond.poisoned.left its ticks left | Anywhere |
@opp.insight.mod | The opponent's stat or modifier, when a roll is aimed at someone present; @opp.tier their tier | A check's opposed targets |
@defence | The defender's defence | The attack check |
@second | A value the blow reads beside its stat, by the name the system or the weapon gives it (What every attack reads, What its attack reads): the second die of a two-stat roll, or any other name (@accuracy, @weapon_die) | The attack check and damage |
@tier | The enemy's tier, 1 to 6 | Enemy templates |
@this | The stat itself; @linked the stat it leans on | A stat's modifier |
@group.Hope.high | A named die's highest face; also .tops (top faces), .ones, .successes, .value | Outcomes and rule cards after a roll |
@total | The roll's total; also @target, @margin (total − target), @measure | Outcomes and rule cards after a roll |
@event.amount | The damage of the blow that set off the card | Damage cards |
@roll.high | The highest die of the attack roll that landed the blow; @roll.low its lowest; @group.First.value a named die. @roll.high + 5 is a High Roll, with no dice of its own | Damage (weapons, abilities, enemies, the player's) |
@thing.ship.hull | A value on the character's first thing of a kind | Anywhere |
How a roll is read
A check has a roll and a way of reading it. The reading turns the dice into the outcome bands' measure:
| Reading | Measure | Options |
|---|---|---|
| Total against a target | A degree: −1 fumble, 0 failure, 1 success, 2 critical | steps: beat the target by this much for a critical, miss by it for a fumble (PF2e: 10). naturals: a top or bottom face on the first die sets or shifts the degree. |
| Total into bands | The total itself | Bands by range: 10+, 7-9, 6- (PbtA). |
| Roll under | A grade: −1 fumble, 0 failure, 1 success, 2 hard, 3 extreme | grades (fractions of the target), what counts as a critical (a 1, rolling exactly the target, doubles), what fumbles, faces that always pass or fail, success levels instead of grades, or the roll's own tens die (roll under but high: higher is better). |
| Highest die | The best face; several top faces count one higher | Forged in the Dark. |
| Count successes | The count | Pairs of a face as extra successes and a critical (tens in a d10 pool). |
On top of any reading, a check can compare two named dice (Hope against Fear): which came up higher adds a word to the outcome and its own effects; equal dice can be a critical (or only from a face up, like doubles of 6 or more), and low equal dice a fumble (double 1s).
Recipes
Fifth-edition advantage, with conditions and exhaustion
(if @edge + clamp(@cond.edge + (if @exhaustion >= 1 then -1 else 0), -1, 1) > 0 then 2d20kh1 else if @edge + clamp(@cond.edge + (if @exhaustion >= 1 then -1 else 0), -1, 1) < 0 then 2d20kl1 else d20) + @stat.mod + @cond.mod
Classic d20's ability check: any number of disadvantages and advantages count once either way.
Pushing yourself for an extra die
if @stat + @spend_push <= 0 then 2d6kl1 else (@stat + @spend_push)d6
Forged in the Dark: zero dots rolls two and keeps the lower; paying the “push” spend (2 Stress) adds a die.
Hope, Fear, and a d6 if you spend Hope
d12[Hope] + d12[Fear] + @stat + (if @spend_hope > 0 then d6 else 0)
Two stat dice: a stat is a die
d(@stat)[First] + d(if @second_stat == 1 then @dexterity else @insight)[Second] + @cond.mod
read: total against @difficulty; matching dice of 6 or more are a critical, double 1s a fumble
attack: d(@stat)[First] + d(@second)[Second] reads: { "second": "@might" }The composer's two stat dice engine: each stat is a die size (d6 to d12), and the second die is the stat a word the judge picks names. A stat shown as a die always reads as a real one: a +1 from gear leaves a d8 a d8, a +2 makes it a d10. A condition that moves a stat by steps is already in @stat: Dazed, one step down on Insight, rolls a d10 Insight as d8. See Conditions that move a stat. A blow's second die is @second, which every attack reads as the fighting stat; a staff whose reads say { "second": "willpower" } rolls WLP + WLP. See What its attack reads.
A number that grows with a class
"values": { "caster": "@classlevel", "kit": "2 * @classlevel", "hp": "5" }A class's own values, under several classes: a caster level, 2 kit points a class level, and +5 Hit Points for holding it at all. Added together across their classes, a wizard 2 / cleric 3 has a caster level of 5. A heroic ability gated on mastery reads @classlevel.sentinel >= 10 or @mastered >= 1. See Numbers across classes.
Hit points by each class's die
"hit_points": "@classfirst * (floor(@own.hit_die / 2) - 1) + @classlevel * (floor(@own.hit_die / 2) + 1)"
Classic d20's class default, worked out once for each class they hold: the first class's whole die at its first level, the die's average every level after, and a later class's average from its first. A Fighter 1 / Wizard 1 gets 10 + 4. See A default worked out per class.
Percentile with bonus and penalty dice
d100t(@edge) read: roll under, target @stat.mod * @difficulty
A hunger pool: some dice are dangerous
(@stat - @hunger)d10>=6 + (@hunger)d10[Hunger]>=6
Count successes across both groups; then a rule card reads @group.Hunger.ones to turn a failure with a 1 on a Hunger die into something worse.
Degrees of success by ten
roll: d20 + @stat.mod read: total against @difficulty, steps 10, naturals 20 / 1 shift the degree
An opposed lie
opposed: { "deception": "10 + @opp.insight.mod" }A lie told to someone present is rolled against their passive Insight instead of a difficulty.