# Blades in the Dark

The `@randsum/games/blades` subpath provides mechanics for [Blades in the Dark](https://www.evilhat.com/home/blades-in-the-dark/), a tabletop RPG about a crew of daring scoundrels seeking their fortunes on the haunted streets of an industrial-fantasy city.

[Official Site](https://www.evilhat.com/home/blades-in-the-dark/)

## Installation

<CodeExample lang="bash" code={`bun add @randsum/games`} />
  <CodeExample lang="bash" code={`npm install @randsum/games`} />
  ## Usage

<CodeExample code={`// Roll with a dice pool of 3
const { result } = roll(3)
console.log(result) // 'critical' | 'success' | 'partial' | 'failure'`} />

<CodeExample code={`roll(0)  // Zero dice: rolls 2d6, keeps lowest
roll(1)  // One die
roll(2)  // Two dice
roll(3)  // Three dice
roll(4)  // Four dice (e.g., with assistance)
roll(5)  // Five dice (e.g., with assistance + push)
roll(6)  // Six dice (maximum pool)`} />

<CodeExample code={`const { result } = roll(2)

switch (result) {
  case 'critical':
    // Multiple 6s - exceptional outcome
    break
  case 'success':
    // Highest die is 6 - full success
    break
  case 'partial':
    // Highest die is 4-5 - success with complication
    break
  case 'failure':
    // Highest die is 1-3 - things go badly
    break
}`} />

## API

### `roll(rating?: number)`

**Input:**

| Parameter | Type | Description |
|---|---|---|
| `rating` | `number` (optional) | Action rating / dice pool size (0-6) |

**Returns:** `GameRollResult` with:

| Property | Type | Description |
|---|---|---|
| `result` | `BladesRollResult` | `'critical' \| 'success' \| 'partial' \| 'failure'` |
| `total` | `number` | Sum of kept dice after modifiers |
| `rolls` | `RollRecord[]` | Raw dice data from the core roller |

## Outcomes

| Outcome | Condition | Description |
|---|---|---|
| `critical` | Multiple 6s | Critical success -- exceptional outcome |
| `success` | Highest die is 6 | Full success -- you do it |
| `partial` | Highest die is 4-5 | Partial success -- you do it, but with a complication |
| `failure` | Highest die is 1-3 | Bad outcome -- things go wrong |

### Zero Dice Pool

When the dice pool is 0, the system rolls 2d6 and keeps only the lowest die. This makes failure much more likely and prevents critical results.

## About this implementation

This package handles the dice roll outcome only. **Position** (controlled, risky, desperate) and **Effect** (limited, standard, great) are narrative mechanics in Blades in the Dark that a caller manages separately — they are not part of the roll output. Use `result` to determine the mechanical outcome, then apply position and effect in your own game logic.

## Error handling

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

- **`ValidationError`** (from `@randsum/roller`) -- numeric input out of range or not finite (e.g., rating of 7 when max is 6)
- **`SchemaError`** (from `@randsum/games/blades`) -- game-specific issues like unmatched outcome tables

<CodeExample code={`try {
  roll(99)
} catch (error) {
  if (error instanceof ValidationError) {
    // Out-of-range rating (must be 0–6)
    console.log(error.code)    // 'VALIDATION_ERROR'
  } else if (error instanceof SchemaError) {
    // Game-specific error
    console.log(error.code)    // 'NO_TABLE_MATCH'
  }
}`} />

## Types

<CodeExample code={``} />

## Schema

This game is powered by a `.randsum.json` spec that defines the dice pool, outcome tiers, and validation rules. The TypeScript code is generated from this spec at build time. See [Schema Overview](https://randsum.dev/games/schema/overview/) for how game specs work.

## Links

- [npm package](https://www.npmjs.com/package/@randsum/games)
- [Source code](https://github.com/RANDSUM/randsum/tree/main/packages/games)