# esx_policejob (/docs/addons/esx_policejob)

esx_policejob turns the `police` job into a playable law-enforcement role: cloakrooms with grade uniforms, an armory where officers buy weapons, a garage for job cars and helicopters, a boss menu, and an F6 menu to search, cuff, drag, fine and impound. Install it when you want the stock ESX police department rather than writing your own.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_policejob` (version `1.0.2`) |
| Side | client + server |
| Job name | `police` (grades `recruit`, `officer`, `sergeant`, `lieutenant`, `boss`) |
| Society | `society_police` (account, datastore and inventory) |
| Hard dependencies | `esx_lib`, `es_extended`, `esx_billing`, `esx_vehicleshop` |
| Also used | `oxmysql`, [esx_society](/docs/addons/esx_society) (boss menu, armory storage), `esx_garage` (impound) |
| Optional | [esx_identity](/docs/core/esx_identity), [esx_license](/docs/addons/esx_license), [esx_service](/docs/addons/esx_service), [esx_status](/docs/addons/esx_status) |

On start the server registers the society with `esx_society:registerSociety` and, when `Config.EnableESXService` is on and `Config.MaxInService` is not `-1`, activates a service limit through esx_service.

### What officers can do [#what-officers-can-do]

- **Cloakroom**: switch between civilian clothes and the uniform for their grade, add a bulletproof vest or a high-visibility vest, and (optionally) pick a custom ped.
- **Armory**: buy the weapons listed for their grade. With armory management enabled they can also deposit and withdraw weapons and items from the shared police storage.
- **Garage**: buy job cars or helicopters for their grade, take them out, and store them again.
- **F6 menu**:
  - Citizen interaction: ID card, body search, handcuff, drag, put in vehicle, take out of vehicle, fine, unpaid bills, and license check (when licenses are enabled).
  - Vehicle interaction: vehicle info, lockpick, impound, search the plate database.
  - Object spawner: cones, barriers, spike strips, boxes and a cash crate.
- **Boss menu**: managed by esx_society at the `BossActions` marker. Money washing is disabled and the uniform editor is enabled.

## Installation [#installation]

<Steps>
<Step>
### Import the SQL [#import-the-sql]

Run `esx_policejob.sql`. It adds the `society_police` rows to `addon_account`, `datastore` and `addon_inventory`, creates the `police` job with five grades, and creates and fills the `fine_types` table.

Translated variants live in `localization/<lang>_esx_policejob.sql` (de, en, es, fi, fr, it, nl, pl, sr). Import only one of them.
</Step>
<Step>
### Start it after its dependencies [#start-it-after-its-dependencies]

```bash title="server.cfg"
ensure oxmysql
ensure es_extended
ensure esx_lib
ensure esx_addonaccount
ensure esx_addoninventory
ensure esx_datastore
ensure esx_society
ensure esx_billing
ensure esx_vehicleshop
ensure esx_policejob
```
</Step>
<Step>
### Turn on the optional integrations [#turn-on-the-optional-integrations]

Set `Config.EnableESXIdentity`, `Config.EnableLicenses`, `Config.EnableESXService` and `Config.EnableESXOptionalneeds` to match the resources you actually run.
</Step>
</Steps>

<Callout type="warn" title="txAdmin recipe installs">
The official ESX recipe (`legacy.sql`) already contains the `police` job, its society rows and the `fine_types` table. If you used the recipe, do not import `esx_policejob.sql` again. It fails with duplicate key and "table already exists" errors.
</Callout>

## Configuration [#configuration]

All options are in `config.lua`.

| Key | Default | Description |
| --- | --- | --- |
| `Config.DrawDistance` | `10.0` | Distance at which markers are drawn. |
| `Config.ActionVehicleDistance` | `2.0` | How close a vehicle must be for vehicle actions in the F6 menu. |
| `Config.MarkerType` | `{ Cloakrooms = 20, Armories = 21, BossActions = 22, Vehicles = 36, Helicopters = 34 }` | Marker type per station point. |
| `Config.MarkerSize` | `{ x = 1.5, y = 1.5, z = 0.5 }` | Marker size. |
| `Config.MarkerColor` | `{ r = 50, g = 50, b = 204 }` | Marker color. |
| `Config.EnablePlayerManagement` | `true` | Enables society management (boss menu). |
| `Config.EnableArmoryManagement` | `false` | Adds weapon and item deposit/withdraw to the armory. |
| `Config.EnableESXIdentity` | `true` | Shows name, date of birth, sex and height from esx_identity on ID cards and in vehicle owner lookups. |
| `Config.EnableESXOptionalneeds` | `false` | Shows the drunk level (from esx_status) in the ID card menu. |
| `Config.EnableLicenses` | `false` | Shows licenses during body search and enables license checks and revocation through esx_license. |
| `Config.EnableHandcuffTimer` | `true` | Uncuffs automatically after `Config.HandcuffTimer`. |
| `Config.HandcuffTimer` | `10 * 60000` | Auto-uncuff delay in milliseconds (10 minutes). |
| `Config.EnableCustomPeds` | `false` | Lets officers pick a ped model from `Config.CustomPeds` in the cloakroom. |
| `Config.EnableESXService` | `false` | Requires officers to be in service (esx_service) before using the armory, garage and F6 menu. |
| `Config.MaxInService` | `-1` | Maximum number of officers in service. `-1` means no limit. |
| `Config.EnableFinePresets` | `false` | `true` uses the `fine_types` categories, `false` shows a free amount and reason input. |
| `Config.Locale` | `GetConvar('esx:locale', 'en')` | Language. |
| `Config.OxInventory` | `ESX.GetConfig().OxInventory` | Read from es_extended, do not change. |

### Stations [#stations]

`Config.PoliceStations` is a table of stations (the default one is `LSPD`). Each station has:

| Field | Description |
| --- | --- |
| `Blip` | `Coords`, `Sprite`, `Display`, `Scale`, `Colour` for the map blip. |
| `Cloakrooms` | List of `vector3` positions. |
| `Armories` | List of `vector3` positions. The server only accepts armory actions within 8 units of one of these. |
| `Vehicles` / `Helicopters` | List of garages. Each has a `Spawner` position, an `InsideShop` preview position and `SpawnPoints` (`coords`, `heading`, `radius`). |
| `BossActions` | List of `vector3` positions for the boss menu. |

### Grade-based tables [#grade-based-tables]

These tables are keyed by **grade name** (`recruit`, `officer`, `sergeant`, `lieutenant`, `boss`). If you rename grades in the database, rename the keys too.

- `Config.AuthorizedWeapons[grade]`: list of `{ weapon = 'WEAPON_X', price = n, components = { ... } }`. `components` holds one price per component index of the weapon in ESX's weapon list; `nil` hides that component.
- `Config.AuthorizedVehicles.car[grade]` and `Config.AuthorizedVehicles.helicopter[grade]`: list of `{ model = 'police3', price = n, props = { ... } }`. `props` is optional and is merged into the stored vehicle properties.
- `Config.CustomPeds`: `shared` entries are available to every grade, plus one list per grade. Each entry has `label`, `maleModel`, `femaleModel`.
- `Config.Uniforms`: one entry per grade plus `bullet_wear` and `gilet_wear`, each with `male` and `female` skinchanger components.

```lua title="config.lua"
-- Let recruits buy a basic cruiser
Config.AuthorizedVehicles.car.recruit = {
    { model = 'police', price = 5000 },
}
```

<Callout type="info">
The server rejects any vehicle whose configured price is `0`. To hand out free cars, use a price of `1` or extend the script.
</Callout>

## Server events [#server-events]

All of these are network events that the client triggers. Every handler checks that the caller has the `police` job and is not off duty, and most also check distance. Do not call them from other resources unless you need the same behavior.

| Event | Arguments | Server checks |
| --- | --- | --- |
| `esx_policejob:handcuff` | `target` | On-duty police, within 5 units of target. Toggles the cuffs. |
| `esx_policejob:drag` | `target` | On-duty police, target is cuffed, within 5 units. |
| `esx_policejob:putInVehicle` | `target` | On-duty police, target is cuffed, within 5 units. |
| `esx_policejob:OutVehicle` | `target` | On-duty police, target is cuffed, within 8 units. |
| `esx_policejob:confiscatePlayerItem` | `target, itemType, itemName, amount` | On-duty police, target cuffed and within 5 units. `itemType` is `item_standard`, `item_account` or `item_weapon`. |
| `esx_policejob:getStockItem` | `itemName, count` | On-duty police near an armory. Withdraws from `society_police`. |
| `esx_policejob:putStockItems` | `itemName, count` | On-duty police near an armory. Deposits into `society_police`. |
| `esx_policejob:impoundOwnedVehicle` | `plate` | Police job, vehicle with that plate within 8 units. Calls `exports['esx_garage']:impoundVehicle(plate)`. |

<Callout type="warn" title="Security">
`confiscatePlayerItem` moves items, money and weapons between players, so it is the event cheaters target first. The current version validates job, duty, distance and cuff state on the server. If you fork the script or add your own actions, keep those checks on the server side and never trust a target ID or amount sent by the client.
</Callout>

## Server callbacks [#server-callbacks]

Callbacks are registered with `xLib.callback.registerCompat`, so they work with both `xLib.callback` and the classic `ESX.TriggerServerCallback`.

| Callback | Arguments | Returns |
| --- | --- | --- |
| `esx_policejob:getOtherPlayerData` | `target, notify` | Table with `name`, `job`, `grade`, `inventory`, `accounts`, `weapons`, plus `dob`, `height`, `sex` (identity), `drunk` (status) and `licenses` (license) when enabled. Empty table unless the target is cuffed and within 5 units. |
| `esx_policejob:getFineList` | `category` | Rows from `fine_types` for that category (cached in memory). |
| `esx_policejob:getVehicleInfos` | `plate` | `{ plate, owner? }`. |
| `esx_policejob:getArmoryWeapons` | none | Weapons stored in the `society_police` datastore. |
| `esx_policejob:addArmoryWeapon` | `weaponName, removeWeapon` | `true` on success. |
| `esx_policejob:removeArmoryWeapon` | `weaponName` | `true` on success, gives the weapon with 500 ammo. |
| `esx_policejob:buyWeapon` | `weaponName, type, componentNum` | `type` `1` buys the weapon, `2` buys a component. Price comes from `Config.AuthorizedWeapons`. |
| `esx_policejob:buyJobVehicle` | `vehicleProps, type` | Inserts the car into `owned_vehicles` with `job = 'police'` and a `POL` plate. |
| `esx_policejob:storeNearbyVehicle` | `plates` | Plate that was stored, or `false`. |
| `esx_policejob:getStockItems` | none | Items in the `society_police` inventory. |
| `esx_policejob:getPlayerInventory` | none | `{ items = ... }` of the caller. |

## Client events [#client-events]

| Event | Triggered by | Effect |
| --- | --- | --- |
| `esx_policejob:handcuff` | server | Toggles the cuffed animation and control locks on the target. |
| `esx_policejob:unrestrain` | server or local | Removes cuffs. Fired automatically on spawn and when the resource stops. |
| `esx_policejob:drag` | server | `(copId)` toggles being dragged by that officer. |
| `esx_policejob:putInVehicle` | server | Warps the cuffed player into a free passenger seat of the closest vehicle (searching from the back). |
| `esx_policejob:OutVehicle` | server | Pulls the cuffed player out of the vehicle. |

## Keybinds [#keybinds]

| Name | Default | Action |
| --- | --- | --- |
| `police:quickactions` | `F6` | Opens the police actions menu. |
| `police:interact` | `E` | Uses the marker you are standing in (cloakroom, armory, garage, boss menu). |

Both are registered through `xLib.addKeybind`, so players can rebind them under **Settings → Key Bindings → FiveM**.

## Integration tips [#integration-tips]

Check whether someone is cuffed before letting them use your own features:

```lua title="server/main.lua"
RegisterNetEvent('myphone:open', function()
    local src = source

    if Player(src).state.isHandcuffed then
        return -- cuffed players cannot use the phone
    end

    TriggerClientEvent('myphone:show', src)
end)
```

```lua title="client/main.lua"
if LocalPlayer.state.isHandcuffed then
    ESX.ShowNotification('You cannot do that while cuffed.')
    return
end
```

Fines are sent with `esx_billing:sendBill` to `society_police`, so paid fines land in the police society account. To pay officers a commission or log fines, hook into [esx_billing](/docs/addons/esx_billing) rather than this resource.

## Common issues [#common-issues]

- **"Off duty" notification everywhere.** The script respects `job.onDuty`. A player whose duty flag is `false` cannot use the armory, garage or F6 menu.
- **Nothing happens when buying a car.** Check that the grade has entries in `Config.AuthorizedVehicles`, the price is above `0`, the player has enough cash, and they stand within 35 units of the garage `InsideShop` or `Spawner`.
- **ID card shows no name, date of birth or height.** `Config.EnableESXIdentity` is `true` by default; if you do not run esx_identity, set it to `false`.
- **Impound does nothing.** Impounding needs `esx_garage` to export `impoundVehicle`. Without it the call silently fails.
- **Duplicate SQL errors.** See the txAdmin recipe note above.

## Related [#related]

- [esx_society](/docs/addons/esx_society)
- [esx_billing](/docs/addons/esx_billing)
- [esx_service](/docs/addons/esx_service)
- [esx_license](/docs/addons/esx_license)
- [Create a job](/docs/guides/create-a-job)
