# Exports and events

Everything another resource may rely on. The MDT and voice dispatch use only what's listed here, so it's safe to build on.

## Records

Events and exports pass these tables. They're the public record: what a plate or name check shows.

### Person

```lua
{
    id = 12, first = 'John', last = 'Smith', dob = '01/02/1990', gender = 'Male', address = '12 Grove St',
    dl = 'Valid', -- 'Valid' | 'Suspended' | 'Revoked' | 'Expired' | 'None'
    dl_valid = true,
    flags = { 'ARMED AND DANGEROUS' },
    warrant = 'Failure to appear', -- or nil
}
```

A name a driver made up comes as `{ first, last, dob, gender, unverified = true }` with no `id`: nobody by that name is on file.

### Vehicle

```lua
{
    id = 7, plate = '4ABC123', make = 'Bravado', model = 'Buffalo S', color = 'Black', year = '2015',
    owner = 'John Smith', owner_id = 12, reg_valid = true, ins_valid = false,
    stolen = false, bolo = true, bolo_reason = 'Armed robbery suspect',
}
```

`model` is the display name ("Buffalo S"), not the spawn name.

### Callout

```lua
{ id = 'domestic_01', name = 'Domestic disturbance', street = 'Grove Street', coords = vector3(...),
  priority = 2, description = 'Neighbours report shouting.' }
```

### Truth

Only c3r knows it, and it only comes out through the server export `getPedSubject`.

```lua
{
    secret = 'drunk',   -- 'clean' | 'nervous' | 'unlicensed' | 'suspended' | 'expired' | 'drunk' | 'drugs' | 'warrant' | 'stolen'
    demeanor = 'polite', -- 'polite' | 'nervous' | 'rude' | 'chatty'
    id = 'hands',        -- asked for a licence: 'hands' it over, 'forgot' it, or gives an 'alias'
    alias = nil,         -- { first, last, dob }: the false name (id = 'alias')
    bac = 0.12,          -- secret = 'drunk' only
}
```

## Server events

Fired with `TriggerEvent` on the server. Listen with `AddEventHandler`. The first argument is always the officer's server id. These are never net events, so a client can't fake them.

| Event | Arguments | When |
|---|---|---|
| `c3r:duty` | `src, on` | An officer goes on or off duty |
| `c3r:contact` | `src, person` | They identify someone (licence handed over, or a name given) |
| `c3r:vehicleContact` | `src, vehicle` | A vehicle comes into a stop or a check |
| `c3r:stopStarted` | `src, person, vehicle` | A traffic stop starts. `person` is `nil`: the driver isn't known until they're identified, then `c3r:contact` fires |
| `c3r:stopEnded` | `src, outcome` | `'cited'`, `'warning'`, `'released'`, `'arrested'`, `'fled'` or `'cancelled'` |
| `c3r:dialogue` | `src, who, text` | Every line of a conversation. `who` is `'Officer'` or `'Subject'` |
| `c3r:note` | `src, text` | A line for the open incident's timeline, e.g. "Breathalyzer, Driver: 0.120 BAC (over the limit)" |
| `c3r:pursuitStarted` | `src, person, vehicle` | A driver runs instead of pulling over |
| `c3r:pursuitEnded` | `src` | Caught or lost (before `c3r:stopEnded`) |
| `c3r:calloutOffered` | `src, callout` | A call offered to this officer |
| `c3r:calloutAccepted` | `src, callout` | They responded |
| `c3r:calloutArrived` | `src` | On scene |
| `c3r:calloutEnded` | `src, completed` | The call ended for them |

```lua
AddEventHandler('c3r:stopEnded', function(src, outcome)
    print(('%s ended a stop: %s'):format(GetPlayerName(src), outcome))
end)
```

## Server exports

The SQL-backed exports answer through a callback, because a call from another resource can't wait on SQL inside c3r.

| Export | Returns | Does |
|---|---|---|
| `isOnDuty(src)` | `boolean` | |
| `setDuty(src, on)` | `ok, reason` | Puts them on or off duty |
| `getPerson(id, cb)` | `cb(person \| nil)` | |
| `getVehicle(id, cb)` | `cb(vehicle \| nil)` | |
| `findVehicle(plate, cb)` | `cb(vehicle \| nil)` | A plate on file |
| `lookupPlate(src, plate, cb)` | `cb(vehicle \| nil)` | Like `findVehicle`, but a plate not on file is looked for near that officer: if the car is there, it gets papers, an owner and a driver on the spot, and `c3r:vehicleContact` fires |
| `createPerson(p, cb)` | `cb(person)` | An existing name + date of birth comes back unchanged |
| `createVehicle(v, cb)` | `cb(vehicle)` | |
| `getPedSubject(netId)` | `person, vehicle \| nil, items, truth` | Everything c3r knows about a ped it made; `nil` if it didn't make it. Returns directly |
| `endStop(src, netId, outcome)` | `boolean` | Ends that officer's stop on that ped. `true` when it did |
| `setCalloutOffers(src, on)` | | Callout offers on or off for that officer |
| `offerCallout(src)` | `ok, reason` | Offer them a call now |

```lua
exports.c3r:lookupPlate(src, '4ABC123', function(vehicle)
    if vehicle and vehicle.stolen then print('Stolen!') end
end)
```

## Client exports

| Export | Returns | Does |
|---|---|---|
| `isOnDuty()` | `boolean` | Reads the `c3rDuty` state bag |
| `getService()` | `'police' \| nil` | The service they're on duty in |
| `setDuty(on)` | `true` | Absolute, never a toggle |
| `isOnCallout()` | `boolean` | |
| `isOfferedCallout()` | `boolean` | |
| `acceptCallout()` | `ok, reason` | |
| `completeCallout()` | `ok, reason` | |
| `requestService(kind)` | `ok, reason` | Sends an AI unit (kinds below) |
| `cancelService(kind)` | `ok, reason` | |
| `requestPursuitBackup(level)` | `ok, reason` | `'light'`, `'medium'`, `'heavy'` or `'air'`. With no chase on, light to heavy sends a cover unit |
| `cancelPursuitBackup()` | `ok, reason` | Cover units leave too |
| `towTarget()` | `vehicle \| nil` | The car a tow would be sent for now: the stop's car, else the nearest empty one within 15 m |
| `pa(cmds)` | `boolean` | Gives PA orders: a command or a list, in the order said. `true` when an order went out |
| `getStop()` | `table \| nil` | The officer's traffic stop: `{ veh_net, ped_net, stopped, mode, people = { { net, seat, role, out } } }`. `mode` is `'front'`, `'behind'`, `'steer'` or `nil` |

**Service kinds:** `ambulance`, `police` (prisoner transport), `tow`, `coroner`, `fire`, `mechanic`, `roadservice`, `animalrescue`, `taxi`, `cover`.

**PA commands:** `pull_over`, `felony`, `driver_out`, `passenger_out`, `everyone_out`, `walk_back`, `kneel`, `hands_up`, `back_in`, `engine_off`. On a felony stop only the first runs.

**Reasons** (the second value when the first is `false`): `off_duty`, `not_available` (unknown kind or its models missing), `already_en_route`, `no_route`, `no_prisoner`, `no_vehicle`, `nobody_to_collect`, `too_far`, `no_pursuit`, `enough_units`, `not_en_route`, `already_dispatched`, `no_backup`.

```lua
local ok, why = exports.c3r:requestService('ambulance')
if not ok then print('No ambulance: ' .. why) end
```

## State bags

| Bag | On | Value |
|---|---|---|
| `c3rDuty` | Player | `true` while on duty (set by the server, replicated) |

```lua
if LocalPlayer.state.c3rDuty then --[[ on duty ]] end
```
