Skip to content

Getting Started with Games

Install @randsum/games and make your first game-specific roll in under a minute.

Terminal window
bun add @randsum/games

This installs @randsum/roller automatically as a dependency.

Import from the game’s subpath and call roll(). That’s it.

import { roll } from '@randsum/games/salvageunion'
const { result, total } = roll('Core Mechanic')
console.log(result.label) // 'Success' | 'Tough Choice' | 'Failure' | ...
console.log(result.description) // Full outcome text
console.log(total) // d20 roll

Pass a table name to roll against any of Salvage Union’s many tables — combat, salvage, critical injuries, and more.

import { roll, VALID_TABLE_NAMES } from '@randsum/games/salvageunion'
roll('Core Mechanic') // Standard action roll
roll('Group Initiative') // Who acts first in combat
roll('Critical Injury') // What happens at 0 HP
console.log(VALID_TABLE_NAMES) // All available tables

The fifth edition package accepts an options object with modifier, advantage/disadvantage, and critical hit tracking.

import { roll } from '@randsum/games/fifth'
// Simple d20 roll
const { result, total } = roll()
console.log(result) // number (d20 roll + any modifier)
// Roll with advantage and a +5 modifier
const attack = roll({
modifier: 5,
rollingWith: 'Advantage'
})
console.log(attack.result) // number
// Track natural 1s and 20s
const crit = roll({
modifier: 3,
crit: true
})
console.log(crit.details.criticals)
// { isNatural1: false, isNatural20: true }

Each game subpath exports a roll() function that:

  1. Accepts game-specific input (a number, an options object, or nothing)
  2. Validates the input — throws ValidationError (from roller) for out-of-range numbers, or SchemaError for game-specific issues
  3. Calls @randsum/roller internally with the right dice configuration
  4. Returns a typed GameRollResult with result, total, rolls, and optionally details

The result field is game-specific — a rich outcome object in Salvage Union (with label, description, and tableName), a numeric total in D&D 5e, a hope/fear determination in Daggerheart.

Every game subpath re-exports SchemaError, GameRollResult, and RollRecord. Import ValidationError from @randsum/roller if you need to catch range errors.

Game roll() functions can throw two types of errors:

  • ValidationError (from @randsum/roller) — for numeric validation like out-of-range values
  • SchemaError (from @randsum/games) — for game-specific issues like unmatched outcome tables
import { roll, SchemaError } from '@randsum/games/salvageunion'
import { ValidationError } from '@randsum/roller'
try {
roll('Not A Real Table')
} catch (e) {
if (e instanceof ValidationError) {
// Numeric input out of range
console.error(e.message)
} else if (e instanceof SchemaError) {
// Game-specific error (e.g., invalid table name)
console.error(e.code, e.message)
}
}
ErrorCodeWhen
ValidationErrorVALIDATION_ERRORNumeric input out of range or not finite
SchemaErrorINVALID_INPUT_TYPEInput value is wrong type
SchemaErrorNO_TABLE_MATCHRoll total doesn’t match any outcome tier
SchemaErrorINPUT_NOT_FOUNDRequired input field is missing