# Callouts SDK

A callout is one Lua file in `c3r/callouts/`. It says what the caller reported, where it can happen, and what really happened: a few hidden stories, one picked at random each time. Copy the example, change it, restart c3r, and it's in rotation.

## How a callout works

- **The report** is what dispatch reads out. Every story shares the same reports, so the report never gives the story away.
- **Locations** are where it can happen: a street name, a scene point, and the exact spot for each person, car and prop.
- **Stories** are what really happened. Each story's `setup` puts people, cars and evidence on scene, writes the objectives, and says what happens when officers do things.
- **People are real.** Everyone you add gets a hidden truth like any driver: name checks find them, searches find their items, and they answer the usual questions plus their own.
- **Several units at once.** Objectives are split between the units on scene, so a team works it faster.

## Your first callout

### 1. Copy the example

`c3r/callouts/_example.lua` is a complete, fully commented callout (a suspicious person by parked cars, with two stories). Copy it to a new file, e.g. `callouts/loitering.lua`, and change its `id` and `name`.

> [!NOTE]
> A file starting with `_` is a template. The example has `enabled = false`, so it never goes out as a real call; set yours to `true`.

### 2. Write the reports

```lua
Callout({
    id = 'loitering',
    name = 'Suspicious person',
    priority = 3,          -- 1 most urgent
    enabled = true,
    reports = {
        'Caller reports someone hanging around the parked cars, looking in the windows.',
        'Suspicious male by the parked cars. Caller is watching from across the road.',
    },
```

### 3. Name the points

`points` lists every spot a location needs, in the order `/c3rplace` walks you through them. A third value of `'car'` means "sit in a car parked where it should be".

```lua
    points = {
        { 'caller', 'The person who rang it in, waiting nearby on their phone.' },
        { 'subject', 'The person they called about.' },
        { 'parked_car', 'The car he was at. Sit in a car parked where it should be.', 'car' },
    },
    locations = {},
```

### 4. Place it in game

Stand at the scene and run `/c3rplace loitering`. It walks you through each point: go to the spot, face the way they should face, press **E**. **Backspace** goes back a point, **X** cancels. When you're done, the location is written into your file's `locations` and used straight away.

Run it again for more locations. `/c3rplace loitering 2` replaces location 2. You need the `command.c3rconfig` ace.

To place points by hand instead, `/c3rpos caller` copies `caller = vector4(x, y, z, heading),` to your clipboard.

### 5. Write the stories

```lua
    stories = {
        thief = {
            weight = 2, -- twice as likely as a weight-1 story
            setup = function(c)
                c:person('caller', { at = 'caller', label = 'Caller', scene = 'WORLD_HUMAN_STAND_MOBILE',
                    questions = { { id = 'c_what', label = 'What did you see?' } },
                    lines = { c_what = { { 'He went car to car, pulling the handles.', set = { c_saw = 'doors' } } } } })
                c:person('subject', { at = 'subject', label = 'Person', secret = 'warrant', demeanor = 'nervous',
                    items = { 'Slim jim' } })

                c:objective('caller', 'Talk to the caller', { blip = 'caller' })
                c:objective('find', 'Find the person they called about', { blip = 'subject' })
                c:objective('deal', 'Deal with them', { after = { 'find' }, who = 'all' })

                c:on('act', function(who, action, arg)
                    if who == 'caller' and action == 'ask' and arg == 'c_what' then c:done('caller') end
                end)
                c:on('contact', function(who) if who == 'subject' then c:done('find') end end)
                c:on('custody', function() c:finish('Suspect arrested for vehicle tampering.') end)
            end,
        },
        lockout = { weight = 1, setup = function(c) --[[ he locked himself out ]] end },
    },
})
```

### 6. Test it

Restart c3r, go on duty, and run `/requestcall loitering`. It puts your call out near you now.

## Rules that keep a callout good

- **Same reports, same starting objectives, for every story.** Story-specific objectives start hidden and are revealed by something found. Otherwise players learn to read the card instead of doing police work.
- **Start with objectives that can be done at once,** so a second unit has real work.
- **Every objective needs something in `c:on(...)` that marks it done.**
- **An error in your file cancels that one call** and prints why in the server console. Nothing else breaks.
- **Check scenario, animation and prop names** against the game files. A wrong one just does nothing.

## Reference

### Callout fields

| Field | Type | What it is |
|---|---|---|
| `id` | `string` | Unique id |
| `name` | `string` | Shown on the offer card |
| `priority` | `number` | 1 is the most urgent |
| `enabled` | `boolean` | `false` keeps it as a template only |
| `reports` | `string[]` | What the caller said; one is picked |
| `points` | `{ name, description, 'car'? }[]` | The spots each location needs |
| `locations` | `{ street, at = vector3, points = { [name] = vector4 } }[]` | Where it can happen |
| `stories` | `{ [key] = { weight, setup = function(c) } }` | What really happened |

### People, cars and evidence

**`c:person(key, opts)`** Someone in the story, made when the first unit gets close.

| Option | Type | What it does |
|---|---|---|
| `at` | `string \| vector3 \| vector4` | A point name, or a position |
| `label` | `string` | What the officer sees before they know a name |
| `model`, `male` | `string`, `boolean` | Ped model, or just the sex |
| `secret` | `string` | Their hidden truth: `'clean'`, `'nervous'`, `'drunk'`, `'drugs'`, `'warrant'`... |
| `demeanor` | `string` | `'polite'`, `'nervous'`, `'rude'` or `'chatty'` |
| `flags`, `warrant` | `string[]`, `string` | What their record shows |
| `items` | `string[]` | What a search finds that matters (shown as evidence) |
| `pockets` | `string[]` | Everyday things a search finds that aren't evidence |
| `age` | `{ min, max }` | How old their ID says they are |
| `home` | `true \| string` | They live at the call's address, or on the street of a point |
| `injured` | `boolean` | On the ground hurt: E is first aid, paramedics take them |
| `scene` | `string \| { dict, name, flag }` | What they're doing: a scenario or an animation |
| `questions` | `{ id, label, needs?, hide?, press?, stress? }[]` | Their own questions under **The call**. Ids start `c_` |
| `lines` | `{ [question id] = lines }` | Their answers. A line can give a fact away: `{ 'He went north.', set = { c_dir = 'north' } }` |

`lines` can also replace their answer to any normal question (e.g. `lines.where`), and a line can be a function that returns the list, decided when it's asked. `needs = { c_fact = value }` shows a question only once they've said it; `press = true` gives a fresh answer each time it's asked; `stress = true` lets nerves show.

**`c:vehicle(key, opts)`** A parked car, on file.

| Option | Type | What it does |
|---|---|---|
| `at` | `string \| vector4` | A point name, or a position |
| `model` | `string` | Spawn name, e.g. `'asea'` |
| `make`, `name`, `color` | `string` | What the plate check says: `'Declasse'`, `'Asea'`, `'Silver'` |
| `paint` | `number` | GTA colour id |
| `owner` | `string` | A person key it's registered to (else someone else) |
| `stolen` | `boolean` | Comes back stolen |
| `locked` | `boolean` | Defaults to `true` |
| `damage` | `'front'` | The front end smashed in |

**`c:clue(key, opts)`** Evidence. Up close, "E Inspect: label" shows `text` and puts it on every unit's MDT.

| Option | Type | What it does |
|---|---|---|
| `at` / `on` | `string` | A point, or a vehicle key to put it on that car |
| `label`, `text` | `string` | What it's called, and what you find |
| `prop` | `string` | An object laid on the ground there; it glows until looked at |
| `hidden` | `boolean` | Not there until `c:reveal(key)` |

### Objectives

**`c:objective(id, text, opts)`** A line on the objective card.

| Option | What it does |
|---|---|
| `after = { ids }` | Must be done first |
| `who = 'one' \| 'all'` | One unit takes it (default), or it's on everyone's card |
| `hint` | Smaller text under it |
| `hidden` | Off the card until `c:reveal(id)` |
| `blip` | A person, vehicle or clue key (or a list) to mark on the map of whoever has it |

`c:done(id)` marks it done, `c:reveal(id)` shows a hidden one (and a clue with the same key), `c:hide(id)` takes it off again, `c:is_done(id)` checks.

### Events

**`c:on(event, fn)`**

| Event | Arguments | When |
|---|---|---|
| `arrive` | `src` | A unit got there |
| `contact` | `person, src` | Someone started talking to a person |
| `act` | `person, action, arg, src` | Anything done with them: `'ask'` (arg = question id), `'licence'`, `'frisk'`, `'cuff'`, `'release'`, `'warn'`, `'cite'`, `'escort'`, `'seat'`... |
| `fact` | `person, fact, value, src` | Something they gave away (a line's `set`) |
| `contact_ended` | `person, outcome, src` | `'released'`, `'arrested'`, `'cited'`, `'warning'`, `'cancelled'` |
| `inspect` | `clue, src` | Evidence looked at, the first time |
| `tick` | `seconds` | Once a second |
| `gone` | `person` | Taken away by a unit, or deleted |
| `custody` | `person, how, src` | A prisoner taken in: `'booked'` or `'transport'` |
| `aid` | `person, what, src` | First aid (`'vitals'`, `'pressure'`, `'cpr'`) or paramedics (`'hospital'`, `'treated'`) |

### Helpers

| Call | Does |
|---|---|
| `c:custody(person)` | They're under arrest: "Take them in" goes on the card until they're booked or transported |
| `c:talking()` | Whether an officer is talking to anyone on the call right now |
| `c:questions(person)` | After giving them a fact from elsewhere: refreshes what the officer talking to them sees |
| `c:who(person)` | Their record on the call: `.facts`, `.cuffed`, `.identified`... |
| `c:name(person)`, `c:plate(vehicle)`, `c:street()` | Real name, plate, the call's street |
| `c:owner(vehicle)` | Who the car's registered to: `{ first, last, address }` |
| `c:apart(a, b)` | Out of each other's earshot (10 m, or either cuffed or in a car) |
| `c:task(person, task, point?)` | `'go'` (walk to a point), `'flee'`, `'surrender'`, `'wander'`, `'scene'` |
| `c:say(person, text)` | A line heard on scene |
| `c:dispatch(text)` | A radio update to the units on it |
| `c:note(text)` | A line on their MDT incident |
| `c:set(k, v)`, `c:get(k)` | The call's own memory |
| `c:finish(summary)` | It's dealt with: completed, the summary on the MDT |

## Sharing

Made a good one? Share it on [Discord](https://discord.gg/brtbtgFJp5). Callouts are yours to share; remember the licence: free to share, never to sell.
