Skip to content

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 ​

LayerDefaultWhat it does
SyncOn whenever Config.Modules.medic is onSends 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
SimulationOff (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_ambulancejob under ESX; qb-ambulancejob, qbx_medical, qbx_ambulancejob under QBCore / Qbox) plus Config.Medic.Compat.conflicts (default wasabi_ambulance, ars_ambulancejob, osp_ambulance, visn_are).
  • Takeover. With Config.Medic.Compat.onConflict = "stop" (default) running conflicts are stopped with StopResource. 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 from server.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's dead really kills. Then esx:onPlayerDeath runs as usual, and after Config.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) and xPlayer.get("isDead"), under QBCore in the metadata inlaststand and isdead. Admin menus that used esx_ambulancejob:revive or hospital:client:Revive use medicrevive <id> instead (ACE command.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.

lua
-- 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/.

OptionDefaultDescription
Config.Medic.Runtime.enabledfalseMain 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.pintrueDebugging only. false gives death back to GTA and makes the simulation useless
Config.Medic.Timeline.scale0.45Multiplies every deterioration rate. 1.0 is realistic. Smaller gives EMS more time
Config.Medic.Timeline.arrestFloorS240Minimum time any cardiac arrest lasts before it's fatal: the CPR window
Config.Medic.Respawn.waitS60Time dead before respawn is offered
Config.Medic.Respawn.bill{ base = 500, …, max = 5000 }Hospital bill, base plus per injury, capped
Config.Medic.Respawn.locationsPillbox Hill, Sandy Shores, Paleto BayRespawn points
Config.Medic.Tiers.jobsambulance = { [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>.enabledtruefalse turns the minigame into a timed action with a fixed result
Config.Medic.Notruf.toDispatchtrueAlso send every emergency call to the dispatch board
Config.Medic.Notruf.requireItemtrueThe emergency call needs the medic_notruf item
Config.Medic.Hospital.enabledtrueIn-game hospital
Config.Medic.Integration.hatchEnabledtrueAllow the escape-hatch exports (applyInjury …)
Config.Medic.Debug.enabledfalseDebug 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 ​

The treatment menu in the NUI

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).

InputDefaultWhat it does
Look at a regionThe label closest to the screen centre is the selected region
G (medic_untersuchen)4 sExamine. 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
/koerperansichtToggle the body labels
/selbstversorgungTreat your own light injuries. Nothing goes to the MDT
/pulsCheck 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 ​

CommandWhat it does
/ziehenDrag
/tragenCarry
/spineboard [ab]Spineboard (needs a second medic who accepts with /anheben)
/absetzenPut down
/trage [auf|ab|zurueck]Stretcher
/einladen · /ausladenLoad into / unload from the ambulance
/hintenRide 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).

CommandTierWhat it does
/aufnehmen [player] ["bed"]2Admit a patient to a bed
/roentgen · /ct [player] [region]3X-ray / CT. Findings go into the patient file as an image
/op start <procedure> [player] · /op wahl <n> · /op abbrechen4 (assistant 3)Surgery: laparotomy, thoracotomy, foreign_body, fracture_fixation, spinal_stabilisation, wound_revision
/entlassen [player]4Discharge

Toxicology ​

  • /drogentest or the medic_tox_screen item: 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.
  • /blutentnahme or the medic_blood_draw item: after 15 seconds the level of each substance goes into the patient file as lab_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

np_* FiveM resources