# Powered by the Apocalypse

The `@randsum/games/pbta` subpath provides generic [Powered by the Apocalypse](https://apocalypse-world.com/) (PbtA) mechanics that work with any PbtA game, including Dungeon World, Monster of the Week, Apocalypse World, Masks, and many others.

[Official Site](https://apocalypse-world.com/)

## Installation

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

<CodeExample code={`// Roll 2d6 + stat modifier
const result = roll({ stat: 2 })
// result.result: 'strong_hit' | 'weak_hit' | 'miss'`} />

<CodeExample code={`// With forward and ongoing bonuses
const result = roll({
  stat: 1,
  forward: 1,  // One-time bonus
  ongoing: 0   // Persistent bonus
})`} />

<CodeExample code={`// Roll with advantage (3d6, keep 2 highest)
const result = roll({
  stat: 2,
  rollingWith: 'Advantage'
})

// Roll with disadvantage (3d6, keep 2 lowest)
const result2 = roll({
  stat: 2,
  rollingWith: 'Disadvantage'
})`} />

<CodeExample code={`const { result, total } = roll({ stat: 2 })

switch (result) {
  case 'strong_hit':
    // 10+ total: complete success
    break
  case 'weak_hit':
    // 7-9 total: success with cost
    break
  case 'miss':
    // 6- total: failure
    break
}`} />

## API

### `roll(input: { stat: number, forward?: number, ongoing?: number, rollingWith?: 'Advantage' | 'Disadvantage' })`

**Input:**

| Property | Type | Description |
|---|---|---|
| `stat` | `number` | Stat modifier (-3 to 5) |
| `forward` | `number` (optional) | One-time bonus (-5 to 5) |
| `ongoing` | `number` (optional) | Persistent bonus (-5 to 5) |
| `rollingWith` | `'Advantage' \| 'Disadvantage' \| undefined` | Roll 3d6, keep 2 highest or lowest |

**Returns:** `GameRollResult` with:

| Property | Type | Description |
|---|---|---|
| `result` | `PbtaRollResult` | `'strong_hit' \| 'weak_hit' \| 'miss'` |
| `total` | `number` | Sum of 2d6 + all modifiers |
| `rolls` | `RollRecord[]` | Raw dice data from the core roller |
| `details` | `PbtaRollDetails` | `{ stat, forward, ongoing, diceTotal }` |

## Outcomes

| Outcome | Total | Description |
|---|---|---|
| `strong_hit` | 10+ | Complete success |
| `weak_hit` | 7-9 | Partial success, success with cost |
| `miss` | 6- | Failure |

## Compatible Systems

This package works with any PbtA game, including:

- **Apocalypse World** -- The original PbtA game
- **Dungeon World** -- Fantasy dungeon crawling
- **Monster of the Week** -- Urban fantasy monster hunting
- **Masks** -- Teen superheroes
- **The Sprawl** -- Cyberpunk missions
- **Urban Shadows** -- Urban supernatural politics
- And many more

## Error handling

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

- **`ValidationError`** (from `@randsum/roller`) -- numeric input out of range or not finite (e.g., `stat: 99` when max is 5)
- **`SchemaError`** (from `@randsum/games/pbta`) -- game-specific issues like invalid `rollingWith` values

<CodeExample code={`try {
  roll({ stat: 99 })
} catch (error) {
  if (error instanceof ValidationError) {
    // Out-of-range stat (must be -3 to 5)
    console.log(error.code)    // 'VALIDATION_ERROR'
  } else if (error instanceof SchemaError) {
    // e.g., invalid rollingWith enum value
    console.log(error.code)    // 'INVALID_INPUT_TYPE'
  }
}`} />

## Types

<CodeExample code={``} />

## Schema

This game is powered by a `.randsum.json` spec that defines the 2d6 resolution, stat/bonus ranges, and outcome tiers. 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)