# esx_jobs (/docs/addons/esx_jobs)

esx_jobs provides simple gather-process-sell jobs: put on work clothes, take a company truck for a deposit, stand in work zones to receive raw items, process them in the next zones, and sell the final product for cash. Use it for starter jobs, or as a data-driven framework for your own farming-style jobs.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_jobs` (version `1.0`) |
| Side | shared config, client + server |
| Dependencies | `esx_lib`, `es_extended`, OneSync (`/onesync`) |
| Also used | `oxmysql`, [esx_addonaccount](/docs/addons/esx_addonaccount) (for the `caution` deposit account) |
| Jobs | `fisherman`, `fueler`, `lumberjack`, `miner`, `slaughterer`, `tailor` |

<Callout type="info">
The official docs note that esx_jobs is being rewritten. The description below matches the current source.
</Callout>

### Job chains [#job-chains]

| Job | Raw → processed → sold | Sale price per unit |
| --- | --- | --- |
| `miner` | `stone` → `washed_stone` → `copper`, `iron`, `gold`, `diamond` (5% drop chance) | copper 5, iron 9, gold 25, diamond 250 |
| `lumberjack` | `wood` → `cutted_wood` → `packaged_plank` | 13 |
| `slaughterer` | `alive_chicken` → `slaughtered_chicken` → `packaged_chicken` | 23 |
| `tailor` | `wool` → `fabric` → `clothe` | 40 |
| `fueler` | `petrol` → `petrol_raffin` → `essence` | 61 |
| `fisherman` | `fish` (from a boat) | 11 |

Money from deliveries is paid as cash (`xPlayer.addMoney`).

## Installation [#installation]

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

`esx_jobs.sql` adds the private `caution` addon account, the seven jobs (including `reporter`) with one `employee` grade each, and the 19 items. The job and item labels in the file are French. Rename them in the database if your server is in another language.

If your database came from the ESX txAdmin recipe, these rows already exist and the import fails with duplicate key errors.
</Step>
<Step>
### Start it [#start-it]

```bash title="server.cfg"
ensure esx_addonaccount
ensure esx_jobs
```

OneSync must be enabled, because work vehicles are spawned on the server.
</Step>
</Steps>

## Configuration [#configuration]

`config.lua` holds the global options. Each job lives in its own file under `jobs/` and adds itself to `Config.Jobs`.

| Key | Default | Description |
| --- | --- | --- |
| `Config.DrawDistance` | `10.0` | Distance at which markers are drawn. |
| `Config.Locale` | `GetConvar('esx:locale', 'en')` | Language. |
| `Config.Jobs` | `{}` | Filled by the files in `jobs/`. |
| `Config.MaxCaution` | `10000` | Highest deposit a vehicle spawner may charge. Spawners above this are refused. |
| `Config.RequestIPL` | `true` | Loads a few map IPLs (`CS1_02_cf_onmission1` to `4`, `id2_14_during_door`, `id2_14_during1`) used by job locations. Set to `false` if another resource already manages them. |
| `Config.PublicZones` | `EnterBuilding`, `ExitBuilding` | Teleport markers available to everyone. Each has `Pos`, `Size`, `Color`, `Marker`, `Blip`, `Name`, `Type = "teleport"`, `Hint` and `Teleport`. |

### Job file format [#job-file-format]

```lua title="jobs/miner.lua"
Config.Jobs.miner = {
    BlipInfos = { Sprite = 68, Color = 5 },

    Vehicles = {
        Truck = { Spawner = 1, Hash = 'rubble', Trailer = 'none', HasCaution = true },
    },

    Zones = {
        Mine = {
            Pos = { x = 2962.40, y = 2746.20, z = 42.39 },
            Size = { x = 5.0, y = 5.0, z = 1.0 },
            Color = { r = 50, g = 200, b = 50 },
            Marker = 1,
            Blip = true,
            Name = TranslateCap('m_rock'),
            Type = 'work',
            Item = {
                {
                    name = TranslateCap('m_rock'),
                    db_name = 'stone',
                    time = 3,
                    max = 7,
                    add = 1,
                    remove = 0,
                    requires = 'nothing',
                    requires_name = 'Nothing',
                    drop = 100,
                },
            },
            Hint = TranslateCap('m_pickrocks'),
            GPS = { x = 289.24, y = 2862.90, z = 42.64 },
        },
        -- cloakroom, vehspawner, vehspawnpt, vehdelete and delivery zones follow
    },
}
```

#### Zone types [#zone-types]

| `Type` | Purpose | Extra fields |
| --- | --- | --- |
| `cloakroom` | Toggle work and civilian clothes. Also toggles the client "on duty" state that unlocks the other zones. | |
| `work` | Gives items over time while the player stands in it on foot. | `Item` |
| `vehspawner` | Spawns the job vehicle for a deposit. | `Spawner`, `Caution` |
| `vehspawnpt` | Where the vehicle appears. | `Spawner`, `Heading` |
| `vehdelete` | Returns the vehicle and refunds the deposit. | `Spawner`, `Caution`, `Teleport` |
| `delivery` | Sells items for cash. | `Item`, `Spawner` |

Zones, spawners and vehicles are linked through the same `Spawner` number. The fisherman uses `Spawner = 2` for the boat and its delivery point.

#### `Item` entries [#item-entries]

| Field | Description |
| --- | --- |
| `name` | Display name. For delivery zones this must be `TranslateCap('delivery')`. |
| `db_name` | Item to give (not used for delivery). |
| `time` | Seconds between ticks. Read from the first entry of the list. |
| `max` | Stop when the player holds this many. |
| `add` | Amount given per tick. |
| `remove` | Amount of `requires` removed per tick. |
| `requires` | Required input item, or `'nothing'`. |
| `requires_name` | Label used in the "not enough" message. |
| `drop` | Chance in percent that `add` is given on a tick. |
| `price` | Cash paid per tick (delivery zones only). |

The first entry drives the timing and the required input. Extra entries in the same zone, like iron, gold and diamond in the miner foundry, are extra outputs from the same input.

#### Vehicles [#vehicles]

| Field | Description |
| --- | --- |
| `Spawner` | Links the vehicle to its `vehspawner` zone. |
| `Hash` | Model name. |
| `Trailer` | Trailer model to attach, or `'none'`. |
| `HasCaution` | Marks the vehicle as a deposit vehicle. |

## Vehicle deposit (caution) [#vehicle-deposit-caution]

When a player uses a `vehspawner` zone, the server takes `Caution` from their **bank** account and stores it in their personal `caution` addon account, then spawns the vehicle. When they return it in the `vehdelete` zone, the refund is the deposit multiplied by the vehicle condition: `(engine health + body health) / 2000`. A destroyed vehicle refunds nothing.

Outstanding deposits are settled automatically when the player changes job, disconnects or the resource stops.

## Server events [#server-events]

| Event | Arguments | Description |
| --- | --- | --- |
| `esx_jobs:startWork` | `zoneIndex, zoneKey` | Starts the work loop for the zone `zoneKey` of the player's current job. The first argument is ignored. |
| `esx_jobs:stopWork` | none | Stops the work loop. |
| `esx_jobs:caution` | `cautionType, zoneKey` | `'take'` spawns a vehicle at a nearby `vehspawner`, `'give_back'` returns the vehicle the player drives at a nearby `vehdelete`. |

The server looks up zones from its own config by job and key, never from client data, and stops the work loop as soon as the player is farther from the zone center than the zone's `Size.x`. Deposit amounts are read from the config and capped by `Config.MaxCaution`.

## Client events [#client-events]

| Event | Arguments | Description |
| --- | --- | --- |
| `esx_jobs:spawnJobVehicle` | `netId, spawnPoint, vehicle` | Sent after the server spawned the vehicle. Attaches the trailer and seats the player. |
| `esx_jobs:vehicleReturned` | `netId, teleport` | Sent after a successful return. Teleports the player if `teleport` is set. |
| `esx_jobs:action` | `job, zone, zoneKey` | Local event fired when the player presses `E` in a zone. |

## Common issues [#common-issues]

- **Work zone does nothing.** Put on the work clothes at the cloakroom first, and step out of your vehicle. Work zones only run on foot.
- **"Not enough" right away.** The zone requires an input item. Process the previous step first.
- **Reporter job has no markers.** `reporter` exists in the SQL and has a public teleport building, but no file under `jobs/`.
- **Deposit not taken or vehicle not spawned.** The player needs enough money in the bank, the spawn point must be free, and `esx_addonaccount` must be running so the `caution` account exists.
- **Items show French names.** Update the labels in the `items` table.

## Related [#related]

- [esx_joblisting](/docs/addons/esx_joblisting)
- [esx_addonaccount](/docs/addons/esx_addonaccount)
- [Create a job](/docs/guides/create-a-job)
- [Add items](/docs/guides/add-items)
