Medic module
The medic module is a medical simulation for the game, plus the sync of patients, injuries, treatments and vital signs into the MDT's patient files.
Status
The sync to the MDT is the tested foundation. The in-game parts (damage, death loop, treatment, minigames, emergency calls, transport, hospital) are written and unit-tested, but not yet accepted on a live server. That's why the simulation is off by default (Config.Medic.Runtime.enabled = false).
Two layers
| Layer | Default | What it does |
|---|---|---|
| Sync | On whenever Config.Modules.medic is on | Sends typed events to POST /api/v1/medic/events (patients, injuries, treatments, vital signs, ECG), the latest citizen state to PUT /api/v1/medic/citizens/:id/state, and reads qualifications from GET /api/v1/medic/qualifications. Needs an unscoped key |
| Simulation | Off (Config.Medic.Runtime.enabled) | Takes over life and death in game: injuries from damage, bleeding, downed and dead states, treatment, transport, hospital |
The sync uses a persistent write-ahead queue. It's checkpointed every persistDebounceMs (2 s) and on resource stop, and retried with back-off on errors. A missing or rejected key never empties the queue. Under pressure only vital-sign samples are dropped, never injuries or treatments. medicstatus shows the queue in the console.
Turning the simulation on
With the simulation on, the simulation decides who dies, not GTA and not your ambulance script. Two scripts writing health would fight each other, so np_mdt doesn't share:
- Detection. At start it checks which ambulance script belongs to your framework (
esx_ambulancejobunder ESX;qb-ambulancejob,qbx_medical,qbx_ambulancejobunder QBCore / Qbox) plusConfig.Medic.Compat.conflicts(defaultwasabi_ambulance,ars_ambulancejob,osp_ambulance,visn_are). - Takeover. With
Config.Medic.Compat.onConflict = "stop"(default) running conflicts are stopped withStopResource. If that fails, the simulation doesn't start, but the MDT sync keeps running. With"refuse"nothing is stopped and the simulation just doesn't start. Cleaner than both: remove the ambulance script fromserver.cfg. - Health. The client keeps the ped's health pinned (
Config.Medic.HealthAuthority:"always"or"while_patient"), with a 1000 HP buffer and GTA's headshot instant-kill turned off. Only the simulation'sdeadreally kills. Thenesx:onPlayerDeathruns as usual, and afterConfig.Medic.Respawn.waitS(60 s) a player can wake up in hospital (with a bill, weakness, and an admission in the MDT). - For other scripts. Downed and dead are exposed the way the ambulance scripts did it: under ESX in the state bag (
isDead,isDowned) andxPlayer.get("isDead"), under QBCore in the metadatainlaststandandisdead. Admin menus that usedesx_ambulancejob:reviveorhospital:client:Reviveusemedicrevive <id>instead (ACEcommand.medicrevive).
Resources that occasionally write health (esx_basicneeds, esx_status, qb-smallresources, qbx_smallresources, esx_jail, qb-prison, see Config.Medic.Compat.healthWriters) only get a start-up warning. Their writes have no effect while health is pinned.
-- config/medic/runtime.lua (or your own file under config/medic/)
Config.Medic.Runtime = { enabled = true }Configuration
The main switches. Everything else is commented in the files under config/medic/.
| Option | Default | Description |
|---|---|---|
Config.Medic.Runtime.enabled | false | Main switch for the simulation |
Config.Medic.Compat.onConflict | "stop" | "stop" or "refuse", see above |
Config.Medic.HealthAuthority | "always" | "while_patient" for servers whose other resources read damage from health |
Config.Medic.Health.pin | true | Debugging only. false gives death back to GTA and makes the simulation useless |
Config.Medic.Timeline.scale | 0.45 | Multiplies every deterioration rate. 1.0 is realistic. Smaller gives EMS more time |
Config.Medic.Timeline.arrestFloorS | 240 | Minimum time any cardiac arrest lasts before it's fatal: the CPR window |
Config.Medic.Respawn.waitS | 60 | Time dead before respawn is offered |
Config.Medic.Respawn.bill | { base = 500, …, max = 5000 } | Hospital bill, base plus per injury, capped |
Config.Medic.Respawn.locations | Pillbox Hill, Sandy Shores, Paleto Bay | Respawn points |
Config.Medic.Tiers.jobs | ambulance = { [0] = 2, [1] = 3, [2] = 4 } | Job grade → care tier |
Config.Medic.Tiers.mdtRanks | {} | MDT rank → care tier. Wins over the job grade while the MDT is reachable |
Config.Medic.Assist.default / .ceiling | "assistiert" / "ausbildung" | Guidance level and the most a player may choose |
Config.Medic.Minigames.<name>.enabled | true | false turns the minigame into a timed action with a fixed result |
Config.Medic.Notruf.toDispatch | true | Also send every emergency call to the dispatch board |
Config.Medic.Notruf.requireItem | true | The emergency call needs the medic_notruf item |
Config.Medic.Hospital.enabled | true | In-game hospital |
Config.Medic.Integration.hatchEnabled | true | Allow the escape-hatch exports (applyInjury …) |
Config.Medic.Debug.enabled | false | Debug commands (also need the ACE np_mdt.medic.debug) |
Care tiers: 1 first aider (carries medic_firstaid_kit or medic_verband), 2 Rettungssanitäter (EMT), 3 Notfallsanitäter (paramedic), 4 Notarzt (emergency physician).
Items follow the pattern medic_<thing> (medic_tourniquet, medic_aed, medic_iv_kit …) and are all listed in config/medic/items.lua. They must exist in your inventory. To use your own item names, set name on an entry.
Treatment in game

Standing next to a downed patient (3 m) with at least tier 1, you see labels on the patient's body. What they say depends on your assist level (/hilfestufe realistisch|assistiert|ausbildung).
| Input | Default | What it does |
|---|---|---|
| Look at a region | The label closest to the screen centre is the selected region | |
G (medic_untersuchen) | 4 s | Examine. The first EMS contact creates the patient file in the MDT |
H (medic_versorgen) | Treatment menu (ox_lib context menu, otherwise the NUI). Only what your tier and your items allow | |
/koerperansicht | Toggle the body labels | |
/selbstversorgung | Treat your own light injuries. Nothing goes to the MDT | |
/puls | Check the pulse of someone lying nearby (anyone) |
The server checks tier, items and range, runs the timer or opens a minigame (CPR, intubation, IV access, chest decompression), consumes items through the inventory bridge, and sends the result to the MDT. The NUI only measures (timing, alignment); scoring happens on the server.
Emergency call
Needs the simulation. An injured player calls for help with F7 / /hilferuf, or by using the medic_notruf item (without the item only if requireItem = false). On-duty players with an EMS job and at least Config.Medic.Notruf.minTier (2) get a blip coloured by triage and a hint with number, postal code or street, and time since the injury. The hint never names an injury.
/notrufannehmen [number] takes the call (without a number: the nearest open call). The blip turns blue with the unit's name for everyone, and the unit gets a route. For postal codes set Config.Medic.Notruf.postal to your postal resource's name (client export getPostal by default).
Transport
| Command | What it does |
|---|---|
/ziehen | Drag |
/tragen | Carry |
/spineboard [ab] | Spineboard (needs a second medic who accepts with /anheben) |
/absetzen | Put down |
/trage [auf|ab|zurueck] | Stretcher |
/einladen · /ausladen | Load into / unload from the ambulance |
/hinten | Ride in the back |
Command names are set in Config.Medic.Transport.commands. An empty name disables the command.
Hospital
There are no coordinates in the config. An admin with the ACE np_mdt.medic.hospital sets the hospital up in any interior by walking around. Each command uses their position and heading:
/medic_hospital building "Pillbox Hill Medical Center"
/medic_hospital room "Station 1"
/medic_hospital bed "Bett 3"
/medic_hospital imaging xray | imaging ct ["Name"]
/medic_hospital theatre "OP 1"
/medic_hospital reception
/medic_hospital list | move <kind> "<name>" | remove <kind> "<name>"The MDT owns which buildings, rooms and beds exist. The resource only stores where they are in the game. A bed created in game shows on the MDT's facility board at once, and moving a patient to another bed on the web moves them in game too (within pollS, 10 s).
| Command | Tier | What it does |
|---|---|---|
/aufnehmen [player] ["bed"] | 2 | Admit a patient to a bed |
/roentgen · /ct [player] [region] | 3 | X-ray / CT. Findings go into the patient file as an image |
/op start <procedure> [player] · /op wahl <n> · /op abbrechen | 4 (assistant 3) | Surgery: laparotomy, thoracotomy, foreign_body, fracture_fixation, spinal_stabilisation, wound_revision |
/entlassen [player] | 4 | Discharge |
Toxicology
/drogentestor themedic_tox_screenitem: a quick test on the nearest patient (2.5 m). After 8 seconds it shows which classes are positive, never a number, and nothing goes to the MDT./blutentnahmeor themedic_blood_drawitem: after 15 seconds the level of each substance goes into the patient file aslab_toxicology.
Debug
With Config.Medic.Debug.enabled = true and the ACE np_mdt.medic.debug:
/medic current state
/medic inject WEAPON_CARBINERIFLE 60 58271
/medic inject drowning 40
/medic contact first EMS contact (creates the file)
/medic reset | status | dump | queue
/medic panel the same as buttons in the NUI
/medic practice practise a minigame without a patient