# Getting Started with Games

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

## Install

<CodeExample lang="bash" code={`bun add @randsum/games`} />
  <CodeExample lang="bash" code={`npm install @randsum/games`} />
  This installs `@randsum/roller` automatically as a dependency.

## Simplest example: Salvage Union

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

<CodeExample code={`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.

<CodeExample code={`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`} />

## Moderately complex: D&D 5e

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

<CodeExample code={`// 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 }`} />

## Every game follows the same pattern

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.

## Error handling

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

<CodeExample code={`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)
  }
}`} />

| Error | Code | When |
|---|---|---|
| `ValidationError` | `VALIDATION_ERROR` | Numeric input out of range or not finite |
| `SchemaError` | `INVALID_INPUT_TYPE` | Input value is wrong type |
| `SchemaError` | `NO_TABLE_MATCH` | Roll total doesn't match any outcome tier |
| `SchemaError` | `INPUT_NOT_FOUND` | Required input field is missing |

## What's next

- **[Games Introduction](https://randsum.dev/games/introduction/)** — overview of all supported game systems
- **[Salvage Union](https://randsum.dev/games/salvageunion/)** — table-based rolls for mech salvage RPG
- **[D&D 5e](https://randsum.dev/games/fifth/)** — ability checks, attacks, and saving throws
- **[Schema Overview](https://randsum.dev/games/schema/overview/)** — how game packages are generated from JSON specs