# esx_ambulancejob (/docs/addons/esx_ambulancejob)

esx_ambulancejob adds a playable EMS job to ESX Legacy: paramedics can revive dead players, heal injured ones, take medical supplies from the hospital pharmacy and buy ambulances and helicopters. Death itself (death screen, bleedout, respawn) is handled by [esx_death](/docs/addons/esx_death); this resource only adds the medic side.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_ambulancejob` (version 1.0.2) |
| Side | client + server |
| Dependencies | `esx_lib`, `esx_death`, `es_extended`, `esx_skin`, `esx_vehicleshop` |
| Optional | `esx_society` (boss menu, society account), `esx_phone` (registers the `ambulance` number) |
| Job name | `ambulance` (grades `ambulance`, `doctor`, `chief_doctor`, `boss`) |
| Items | `medikit`, `bandage` |

In-game features:

- **F6 menu** (`/ambulance` command, rebindable key mapping): revive, small heal (bandage), big heal (medikit), put a patient in the closest vehicle, search a nearby dead player.
- **Hospital markers**: cloakroom (go on and off duty in uniform), boss actions, pharmacy, vehicle and helicopter garages, fast-travel elevators.
- **Dead player blips** for on-duty medics, plus a distress blip when a downed player sends a distress signal through esx_death.
- **Usable items**: `medikit` restores full health, `bandage` restores one eighth of max health.

## Installation [#installation]

<Steps>
<Step>

### Import the SQL [#import-the-sql]

Run `esx_ambulancejob.sql`. It creates the `ambulance` job and its 4 grades, the `society_ambulance` account, inventory and datastore, and the `medikit` and `bandage` items. Translated variants live in `localization/`.

</Step>
<Step>

### Check the users column [#check-the-users-column]

The SQL also runs `ALTER TABLE users ADD is_dead`. If esx_death already created that column (it does with `Config.AutoMigrate = true`), the statement fails with a duplicate column error. Remove that last statement before importing, or run the file statement by statement.

</Step>
<Step>

### Ensure the resource [#ensure-the-resource]

Start it after its dependencies:

```bash title="server.cfg"
ensure esx_lib
ensure es_extended
ensure esx_skin
ensure esx_vehicleshop
ensure esx_death
ensure esx_society
ensure esx_ambulancejob
```

</Step>
</Steps>

## Configuration [#configuration]

All keys live in `config.lua`.

| Key | Default | Description |
| --- | --- | --- |
| `Config.DrawDistance` | `10.0` | Distance at which markers are drawn. |
| `Config.Debug` | `false` | Prints client debug messages (duty, blips, healing). |
| `Config.DebugDeath` | `false` | Prints server debug lines for every revive attempt, including why it was blocked. |
| `Config.Marker` | `{type = 1, x = 1.5, y = 1.5, z = 0.5, r = 102, g = 0, b = 102, a = 100, rotate = false}` | Default marker style for hospital points. |
| `Config.ReviveReward` | `700` | Cash paid to the medic per successful revive. `0` disables the reward. |
| `Config.LoadIpl` | `true` | Loads the morgue IPL. Disable if you use an IPL loader such as bob74_ipl. |
| `Config.Locale` | `GetConvar('esx:locale', 'en')` | Language. |
| `Config.DistressBlip` | `{Sprite = 310, Color = 48, Scale = 2.0}` | Blip shown to medics for a distress call. |
| `Config.EnablePlayerManagement` | `false` | Shows the esx_society boss menu for the `boss` grade. |
| `Config.PharmacyItems` | medikit, bandage | Menu entries `{ title = 'Medikit', item = 'medikit' }`. Only `medikit` and `bandage` are accepted by the server. |
| `Config.MaxPharmacyTake` | `5` | Max amount per pharmacy request. |
| `Config.PharmacyCooldown` | `5000` | Cooldown between pharmacy requests in ms. |
| `Config.Hospitals` | `CentralLosSantos` | Hospital definitions, see below. |
| `Config.AuthorizedVehicles` | see below | Buyable vehicles per type and grade. |

### Config.Hospitals [#confighospitals]

Each hospital has these fields:

| Field | Description |
| --- | --- |
| `Blip` | `{ coords, sprite, scale, color }` map blip. |
| `AmbulanceActions` | List of `vector3` points for the cloakroom / boss menu. |
| `Pharmacies` | List of `vector3` points. The server checks the medic is within 8 m of one. |
| `Vehicles`, `Helicopters` | List of `{ Spawner, InsideShop (vector4), Marker, SpawnPoints = {{ coords, heading, radius }} }`. |
| `FastTravels` | `{ From, To = { coords, heading }, Marker }` teleport on entering the marker. |
| `FastTravelsPrompt` | Same as `FastTravels` plus `Prompt`; the player must press the key. |

### Config.AuthorizedVehicles [#configauthorizedvehicles]

Keyed by vehicle type (`car`, `helicopter`) and then by job grade name:

```lua title="config.lua"
Config.AuthorizedVehicles = {
    car = {
        ambulance = { { model = 'ambulance', price = 5000 } },
        boss = { { model = 'ambulance', price = 2000 } },
    },
    helicopter = {
        doctor = { { model = 'buzzard2', price = 150000 } },
    },
}
```

An entry can also carry a `props` table; it is merged into the vehicle properties saved in `owned_vehicles`. Purchased vehicles get an `AMB` plate prefix and are stored with `job = 'ambulance'`.

<Callout type="info" title="Removed keys">
`Config.EarlyRespawnTimer`, `Config.BleedoutTimer`, `Config.RespawnPoints`, `Config.RemoveWeaponsAfterRPDeath`, `Config.RemoveCashAfterRPDeath`, `Config.RemoveItemsAfterRPDeath`, `Config.EarlyRespawnFine*` and the death camera zoom no longer exist here. They live in `esx_death/config.lua`.
</Callout>

## Server events [#server-events]

These are net events, so any client can trigger them. Each one validates the caller on the server, which is why custom scripts should not bypass them.

### esx_ambulancejob:revive [#esx_ambulancejobrevive]

```lua
TriggerServerEvent('esx_ambulancejob:revive', targetServerId)
```

| Name | Type | Description |
| --- | --- | --- |
| `targetServerId` | `number` | Server ID of the dead player. |

<Callout type="warn" title="Client-triggerable">
Revives a player and pays `Config.ReviveReward`. The server requires: caller has job `ambulance` and is on duty, caller is not dead, 8 second rate limit, target within 8 m and dead (according to esx_death), caller owns a `medikit` (one is consumed). To revive from server code use `exports.esx_death:Revive(target, reason)` instead.
</Callout>

### esx_ambulancejob:heal [#esx_ambulancejobheal]

```lua
TriggerServerEvent('esx_ambulancejob:heal', targetServerId, healType)
```

| Name | Type | Description |
| --- | --- | --- |
| `targetServerId` | `number` | Player to heal (within 8 m). |
| `healType` | `'small' \| 'big'` | `small` uses a bandage, `big` uses a medikit. |

On-duty medics only, 3 second rate limit. On success the server sends `esx_ambulancejob:heal` to the target client.

### esx_ambulancejob:putInVehicle [#esx_ambulancejobputinvehicle]

```lua
TriggerServerEvent('esx_ambulancejob:putInVehicle', targetServerId)
```

Warps the target (within 8 m) into the closest free seat of the nearest vehicle. On-duty medics only, 3 second rate limit.

### esx_ambulancejob:giveItem [#esx_ambulancejobgiveitem]

```lua
TriggerServerEvent('esx_ambulancejob:giveItem', itemName, amount)
```

<Callout type="warn" title="Client-triggerable">
Gives pharmacy items. The server only accepts `medikit` or `bandage`, an amount between 1 and `Config.MaxPharmacyTake`, an on-duty medic within 8 m of a pharmacy, and enforces `Config.PharmacyCooldown`. Rejected attempts are printed as a warning.
</Callout>

### esx_ambulancejob:svsearch [#esx_ambulancejobsvsearch]

```lua
TriggerServerEvent('esx_ambulancejob:svsearch')
```

Sends `esx_ambulancejob:clsearch` to every dead player within 3 m of the medic. esx_death handles that client event.

## Client events [#client-events]

Sent by the server; listen to them if you build a custom MDT or HUD.

| Event | Arguments | When |
| --- | --- | --- |
| `esx_ambulancejob:heal` | `healType`, `quiet` | Heals the local ped (`small` = +1/8 max health, `big` = full). |
| `esx_ambulancejob:useItem` | `itemName` | Player used a `medikit` or `bandage`. |
| `esx_ambulancejob:putInVehicle` | none | Warp into the nearest vehicle. |
| `esx_ambulancejob:PlayerDead` | `playerId` | Sent to all medics when esx_death marks a player dead. |
| `esx_ambulancejob:PlayerNotDead` | `playerId` | Sent to all medics when a player is no longer dead. |
| `esx_ambulancejob:PlayerDistressed` | `playerId`, `coords` | Distress signal from a downed player. |
| `esx_ambulancejob:setDeadPlayers` | `deadPlayers`, `locations` | Local event that rebuilds the dead player blips. |

```lua title="client/mdt.lua"
RegisterNetEvent('esx_ambulancejob:PlayerDistressed', function(playerId, coords)
    print(('Distress call from %s at %.1f, %.1f'):format(playerId, coords.x, coords.y))
end)
```

## Server callbacks [#server-callbacks]

Registered with `xLib.callback.registerCompat`, so they work with `xLib.callback` and `ESX.TriggerServerCallback`.

| Callback | Arguments | Returns |
| --- | --- | --- |
| `esx_ambulancejob:getItemAmount` | `item` | Count of that item in the caller's inventory. |
| `esx_ambulancejob:getDeadPlayers` | none | `deadPlayers, locations` from esx_death (empty table if the caller is not an on-duty medic). |
| `esx_ambulancejob:buyJobVehicle` | `vehicleProps`, `type` | `true` when bought. Validates grade, price, money and distance to the shop. |
| `esx_ambulancejob:storeNearbyVehicle` | `plates` | Result of `es_extended:StoreJobVehicle`. |

## Commands [#commands]

| Command | Side | Description |
| --- | --- | --- |
| `/ambulance` | client | Opens the mobile EMS menu. Mapped to `F6` by default (players can rebind it in the FiveM key settings). Only works for the `ambulance` job while alive. |

## Integration tips [#integration-tips]

- **Revive from other scripts**: use the esx_death export on the server.

  ```lua title="server/main.lua"
  local ok = exports.esx_death:Revive(playerId, 'admin')
  if ok then
      print(('Revived %s'):format(playerId))
  end
  ```

- **Duty**: the server checks `xPlayer.job.onDuty ~= false`. If you use a duty script, set ESX duty through it so medics who are off duty cannot revive.
- **Uniforms** come from the `skin_male` / `skin_female` columns of `job_grades` and are applied through [esx_skin](/docs/core/esx_skin).
- **Society money**: the resource registers `society_ambulance` with [esx_society](/docs/addons/esx_society) when that resource is present.

## Common issues [#common-issues]

- **Duplicate column `is_dead`** when importing the SQL: the column already exists (esx_death). Skip the `ALTER TABLE` statement.
- **Revive or heal does nothing**: the player is not on duty, has no `medikit`/`bandage`, is too far away, or hit the rate limit. Turn on `Config.DebugDeath`.
- **F6 does nothing**: the key was rebound in the FiveM settings, or `ESX.PlayerData.dead` is true.
- **Pharmacy returns nothing**: you added an item to `Config.PharmacyItems` that is not `medikit` or `bandage`, or requested more than `Config.MaxPharmacyTake`.

## Related [#related]

- [esx_death](/docs/addons/esx_death)
- [esx_society](/docs/addons/esx_society)
- [esx_vehicleshop](/docs/addons/esx_vehicleshop)
- [Create a job](/docs/guides/create-a-job)
- [Add items](/docs/guides/add-items)
