Skip to content

The @randsum/games/fifth subpath provides Dungeons & Dragons 5th Edition mechanics including ability checks, saving throws, and attack rolls with full support for advantage and disadvantage.

Terminal window
bun add @randsum/games
import { roll } from '@randsum/games/fifth'
// Basic roll with modifier
const result = roll({ modifier: 5 })
// result.result: 6-25 (d20 + 5)
import { roll } from '@randsum/games/fifth'
// Roll with advantage (2d20, keep highest, + modifier)
const result = roll({
modifier: 5,
rollingWith: 'Advantage'
})
import { roll } from '@randsum/games/fifth'
// Roll with disadvantage (2d20, keep lowest, + modifier)
const result = roll({
modifier: -2,
rollingWith: 'Disadvantage'
})

roll(input?: { modifier?, rollingWith?, crit? })

Section titled “roll(input?: { modifier?, rollingWith?, crit? })”

Input:

PropertyTypeDescription
modifiernumber (optional)Ability/skill modifier (-30 to +30)
rollingWith'Advantage' | 'Disadvantage' | undefinedRoll 2d20, keep highest or lowest
critboolean (optional)When set, enables natural 1/20 detection in details.criticals

Returns: GameRollResult<FifthRollResult, FifthRollDetails> with:

PropertyTypeDescription
resultFifthRollResult (number)Final total (d20 + modifier)
totalnumberSame as result
rollsRollRecord[]Raw dice data from the core roller
detailsFifthRollDetailsContains criticals (see below)

FifthRollDetails:

PropertyTypeDescription
criticals{ isNatural1: boolean, isNatural20: boolean } | undefinedPresent when crit input is set
  • Normal roll: 1d20 + modifier
  • Advantage ('Advantage'): Roll 2d20, drop lowest, + modifier (equivalent to 2d20L+modifier)
  • Disadvantage ('Disadvantage'): Roll 2d20, drop highest, + modifier (equivalent to 2d20H+modifier)

The modifier is validated to be a finite number in the range -30 to +30.

import { roll } from '@randsum/games/fifth'
// Attack roll with +5 to hit
roll({ modifier: 5 })
// Attack with advantage
roll({ modifier: 5, rollingWith: 'Advantage' })
// Saving throw with -1 penalty and disadvantage
roll({ modifier: -1, rollingWith: 'Disadvantage' })
// Perception check with +3 Wisdom
roll({ modifier: 3 })

The @randsum/games/fifth package handles d20 action rolls. For ability score generation (4d6, drop lowest), use the core roller directly:

import { roll } from '@randsum/roller'
// Generate one ability score: 4d6, drop lowest
const score = roll('4d6L')
console.log(score.total) // 3-18
// Generate all six scores
const scores = Array.from({ length: 6 }, () => roll('4d6L').total)
console.log(scores) // e.g. [14, 12, 15, 8, 10, 13]

Game roll() can throw two types of errors:

  • ValidationError (from @randsum/roller) — numeric input out of range or not finite (e.g., modifier: Infinity)
  • SchemaError (from @randsum/games/fifth) — game-specific issues like invalid rollingWith values
import { roll, SchemaError } from '@randsum/games/fifth'
import { ValidationError } from '@randsum/roller'
try {
roll({ modifier: Infinity })
} catch (error) {
if (error instanceof ValidationError) {
// Non-finite modifier value
console.log(error.code) // 'VALIDATION_ERROR'
} else if (error instanceof SchemaError) {
// e.g., invalid rollingWith enum value
console.log(error.code) // 'INVALID_INPUT_TYPE'
}
}
import type { FifthRollResult, FifthRollDetails, GameRollResult, RollRecord } from '@randsum/games/fifth'
import { SchemaError } from '@randsum/games/fifth'

This game is powered by a .randsum.json spec that defines the d20 resolution, advantage/disadvantage, and modifier validation. The TypeScript code is generated from this spec at build time. See Schema Overview for how game specs work.