> ## Documentation Index
> Fetch the complete documentation index at: https://docs.awokenlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fun commands

> /roll, /coinflip, /8ball, /random - 3D-only RP helpers with a webhook audit trail.

Four toggleable RP helpers ship with Awoken Chat. Each renders **only as 3D floating text above the sender** - no chat row, ever. Recipients are filtered server-side by proximity so far-away players don't even receive the event.

| Command                               | Result format                                                  | Default colour |
| ------------------------------------- | -------------------------------------------------------------- | -------------- |
| `/roll [max]`                         | `"8/10"`                                                       | White          |
| `/coinflip`                           | `"Heads"` or `"Tails"`                                         | White          |
| `/8ball <question>` (or `/eightball`) | One of the configured answers, e.g. `"Reply hazy, try again."` | White          |
| `/random a, b, c`                     | One of the options, e.g. `"Sam"`                               | White          |

<Tip>
  Because the result is 3D-only and never enters chat, there's no rollback / audit unless you set `webhooks.funCommands`. That webhook IS the audit trail - critical if players are using `/roll` for skill checks, conflict resolution, etc. See [**Webhooks**](/resources/awoken-chat/configuration/webhooks#fun-commands-log).
</Tip>

## Config block

```lua config.lua theme={"dark"}
funCommands = {
    roll      = true,    -- /roll [max=6]            -> "8/10"
    coinflip  = true,    -- /coinflip                -> "Heads"
    eightball = true,    -- /8ball <question>        -> "Reply hazy, try again."
    random    = true,    -- /random a, b, c          -> "Sam"

    rollMax        = 100,    -- hard ceiling on the /roll max arg
    renderDistance = 25.0,   -- metres the 3D text is visible (per-fun-command)

    rollColor      = '#FFFFFF',
    coinflipColor  = '#FFFFFF',
    eightballColor = '#FFFFFF',
    randomColor    = '#FFFFFF',

    rollDurationSec      = 5,
    coinflipDurationSec  = 5,
    eightballDurationSec = 7,
    randomDurationSec    = 5,

    eightballAnswers = {
        -- 20 canonical Mattel answers (10 affirmative / 5 non-committal / 5 negative)
        -- ...
    },
},
```

### Per-command toggles

Setting any of `roll` / `coinflip` / `eightball` / `random` to `false` removes the slash command entirely from the server **and** removes the suggestion picker entry. The command will print `"No such command"` in the F8 console if a player tries to use it.

```lua theme={"dark"}
funCommands = {
    roll      = true,
    coinflip  = false,   -- /coinflip is gone, picker doesn't list it
    eightball = true,
    random    = true,
    -- ...
},
```

### `/roll` config

| Key               | Default     | Notes                                                                                                                                                                                |
| ----------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `roll`            | `true`      | Master toggle.                                                                                                                                                                       |
| `rollMax`         | `100`       | **Hard ceiling** on the `max` arg. `/roll 9999` silently clamps to `/roll <rollMax>`. Not a default - if the player types no arg they roll 1..6, but typing past the ceiling clamps. |
| `rollColor`       | `'#FFFFFF'` | 3D text colour.                                                                                                                                                                      |
| `rollDurationSec` | `5`         | Seconds the result stays visible. Clamped client-side to `[0.25, 60]`.                                                                                                               |

```text theme={"dark"}
/roll       -> 1..6   (default die)
/roll 20    -> 1..20  (D20)
/roll 100   -> 1..100 (percentile)
/roll 9999  -> 1..100 (clamped to rollMax)
/roll 1     -> 1..2   (silently bumped, can't roll a 1-sided die)
```

### `/coinflip` config

| Key                   | Default     | Notes            |
| --------------------- | ----------- | ---------------- |
| `coinflip`            | `true`      | Master toggle.   |
| `coinflipColor`       | `'#FFFFFF'` | 3D text colour.  |
| `coinflipDurationSec` | `5`         | Seconds visible. |

No args. Always rolls 50/50 Heads/Tails.

### `/8ball` config

| Key                    | Default          | Notes                                                                                           |
| ---------------------- | ---------------- | ----------------------------------------------------------------------------------------------- |
| `eightball`            | `true`           | Master toggle. Both `/8ball` and `/eightball` route to the same handler.                        |
| `eightballColor`       | `'#FFFFFF'`      | 3D text colour.                                                                                 |
| `eightballDurationSec` | `7`              | Defaults to 7s (not 5s) - the answer is a full sentence and reads slower than `X/Y` or `Heads`. |
| `eightballAnswers`     | *(20 canonical)* | The answer pool. See below.                                                                     |

The question text is ignored - it's purely flavour. But the player must provide *something* - empty `/8ball` notifies "Ask a yes/no question." and doesn't fire.

#### Customising answers

The shipped 20 are the canonical Mattel set (10 affirmative / 5 non-committal / 5 negative). Replace, add, or remove freely:

```lua theme={"dark"}
eightballAnswers = {
    -- Affirmative
    'It is certain.', 'It is decidedly so.', 'Without a doubt.',
    'Yes, definitely.', 'You may rely on it.', 'As I see it, yes.',
    'Most likely.', 'Outlook good.', 'Yes.', 'Signs point to yes.',
    -- Non-committal
    'Reply hazy, try again.', 'Ask again later.',
    'Better not tell you now.', 'Cannot predict now.',
    'Concentrate and ask again.',
    -- Negative
    "Don't count on it.", 'My reply is no.', 'My sources say no.',
    'Outlook not so good.', 'Very doubtful.',
},
```

<Tip>
  The pick is **uniform random**. The traditional 10/5/5 affirmative/maybe/negative weighting falls out of how many of each you list. Want a heavily-negative 8-ball? List 15 negatives and 3 affirmatives. Duplicates are valid - list "Yes." three times if you want it to appear \~3× more often than other single-entry answers.
</Tip>

Empty list / removed key → falls back to the hardcoded 20. Prevents bricking `/8ball` via a config typo.

### `/random` config

| Key                 | Default     | Notes            |
| ------------------- | ----------- | ---------------- |
| `random`            | `true`      | Master toggle.   |
| `randomColor`       | `'#FFFFFF'` | 3D text colour.  |
| `randomDurationSec` | `5`         | Seconds visible. |

```text theme={"dark"}
/random Alex, Sam Smith, Jamie  ->  "Sam Smith"
/random Alex Sam Jamie          ->  "Alex"    (space-separated fallback)
/random Alex                    ->  Notify: "Give at least two options"
```

**Parsing rules**:

* If the args contain a `,` → comma-separated. Whitespace inside an option is preserved (`"Sam Smith"` is one entry).
* If no `,` → falls back to space-separated.
* Minimum 2 options; fewer notifies and doesn't fire.
* Hard caps: 64 options max, 60 chars per option (option strings longer than that are truncated with `...` to keep the 3D backdrop sane).

## Shared config (applies to all four)

### `renderDistance`

```lua theme={"dark"}
renderDistance = 25.0,
```

Metres the 3D text is visible. Overrides the global `threeD.renderDistance` for these four commands only - useful if you want dice rolls to feel like a table-top range (e.g. `5.0` m) while leaving `/me` at full conversation distance.

The server filters the recipient set by this distance **before sending the event**, so far-away clients don't even pay queue/rendering cost.

### `rollMax`

Already covered above but worth re-emphasising: this is the only **hard ceiling** anywhere in the fun-command config. Default 100 means `/roll 9999` silently clamps to `/roll 100`, not an error. Bump to 1000 for thousand-sided dice; drop to 6 to force every roll to be 1-6 regardless of what the player types.

### `*DurationSec`

Per-command seconds the 3D text stays floating. Clamped client-side to `[0.25, 60]`. `nil` falls back to the global `threeD.durationMs` (5 seconds default).

`/8ball` defaults to 7s on purpose - sentence-length text reads slower than `X/Y`.

### Colours

All four `*Color` keys default to white to match the `/me` channel's default text colour, so overhead text reads as one visual family. Override per-command if you want a coloured dice (`#FCD34D` amber) or coin (`#7DD3FC` cyan) distinct from RP `/me`.

## Audit webhook

Set `webhooks.funCommands` to log every fire to Discord:

```lua theme={"dark"}
webhooks = {
    enabled     = true,
    funCommands = 'https://discord.com/api/webhooks/...',
},
```

Each command posts a colour-coded embed (amber for `/roll`, cyan for `/coinflip`, purple for `/8ball`, teal for `/random`) with:

* The sender's real character name + server id + avatar (the audit point - chat saw none of this).
* Command + result inline.
* Question (`/8ball` only) or Options (`/random` only) below.
* License + Discord ping for cross-session lookup.

See [**Webhooks**](/resources/awoken-chat/configuration/webhooks#fun-commands-log) for the full embed shape.
