Configuration Reference
Every setting in oxide-medical, what it does, and what it is set to out of the box.
Every setting in oxide-medical, what it does, and what it is set to out of the box.
How Configuration Works Here
oxide-medical is configured entirely in files. There is no settings menu, no admin panel and no database-backed configuration — edit a file, restart the resource, and the change is live.
Every config file is escrow-ignored, so all of it stays editable after installation.
| File | Holds |
|---|---|
shared/config.lua | System toggles: immunity, damage over time, blood decals, treatment, and the trigger layer |
shared/config/wounds.lua | Weapon-to-class map, bone-to-body-part map, fracture rules |
shared/config/woundtypes.lua | The wound catalog and the class-to-wound rules |
shared/config/fractures.lua | The fracture catalog |
shared/config/diseases.lua | The disease catalog |
shared/config/sickness.lua | The sickness catalog and the needs-sickness rules |
shared/config/symptoms.lua | What each symptom token does on screen |
shared/config/vitals.lua | The vitals and cardiac arrest model |
shared/config/notifications.lua | Every vague message a player can receive |
locales/en.json | Every piece of text the resource can show a player |
Adding your own conditions needs no code. The catalogs are plain tables. Add a fracture, a disease or a sickness and the rest of the system — persistence, effects, symptoms, notifications, treatment, damage, the admin tools — picks it up automatically.
Where the Wording Lives
Anything a player can read lives in locales/en.json. The config files do not contain sentences — they contain locale keys, which are short labels like fracture.broken_leg_left that point at a line of text in the locale file.
-- shared/config/fractures.lua locales/en.json
label = 'fracture.broken_leg_left' "fracture.broken_leg_left": "Broken Left Leg",To change what a player sees, edit the right-hand side in locales/en.json. To translate the resource, copy locales/en.json to locales/de.json (or whichever language), translate the right-hand side of every line, and set the server convar setr ox:locale "de". Leave the left-hand side — the key — exactly as it is; that is what the code looks for.
You can also skip the locale file entirely. If you put a sentence where a key is expected, it is shown as written:
label = 'Shattered Left Leg' -- shown verbatim; no locale entry neededThat makes it safe to add your own fracture or disease without touching locales/en.json.
If some text in a locale line contains %s, %d or %.1f, leave those markers in place — they are where the resource inserts a name or a number. You can move them within the sentence, but do not delete them or change how many there are.
System Settings — shared/config.lua
General
| Setting | Default | What it does |
|---|---|---|
Config.Debug | false | Prints diagnostic lines to the console. |
Config.AssignBloodTypeOnFirstLoad | true | Assign a random blood type and DNA hash the first time a character's record is created. Off: both stay empty until something sets them. |
Config.WoundReportRateLimitMs | 1500 | Minimum gap between accepted wound reports per player. Caps how fast one player can register new injuries. |
Config.ExpiryCheckIntervalMs | 30000 | How often the server sweeps for conditions whose expiry has passed. |
Config.Immune — the immune stat
A 0–100 stat on every record. Lower immunity means illness takes hold more easily.
| Field | Default | What it does |
|---|---|---|
start | 100 | What a new character starts at. |
min / max | 0 / 100 | Clamp range. |
needsHitPenalty | 3 | Immunity lost per evaluation while a need is in the danger zone. |
regenPerTick | 2 | Immunity regained per evaluation while every need is healthy. |
minSusceptibility | 0.3 | Floor on acute-exposure chance. Even a fully immune player keeps this fraction of the base chance for contagion and environmental infection — nobody is completely bulletproof. |
How it scales. The needs-sickness rules multiply their base chance by (100 - immune) / 100, so a player at 100 immunity effectively cannot catch those. Contagion and environmental sources use the same curve but floored by minSusceptibility.
Config.Damage — damage over time
Untreated severe conditions drain health.
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
tickIntervalMs | 5000 | How often damage is applied. |
minHealth | 0 | The floor damage can push a player to. 0 lets them go down through your normal death handling. |
perTick is HP lost per tick, per category, per severity. Anything not listed deals nothing:
| Category | Minor (1) | Moderate (2) | Severe (3) | Critical (4) |
|---|---|---|---|---|
| Fracture | — | — | — | 1 |
| Disease | — | — | 1 | 3 |
| Sickness | — | — | — | — |
| Wound | — | 1 | 2 | 3 |
Damage from every active condition is summed, so a player with a critical wound and a critical disease loses 6 HP per tick. It pauses while they are downed, dead or in godmode.
This is the setting most servers should look at first. It decides how punishing untreated injury is.
Config.BleedDecal — blood on the ground
| Field | Default | What it does |
|---|---|---|
MinTier | 2 | Minimum bleeding tier before decals drop. |
IntervalMs | 4000 | Minimum gap between decals. |
LifetimeSec | 60 | How long each decal lasts. |
Purely cosmetic and client-side. A decal is also skipped unless the player has moved at least half a metre, so standing still doesn't create a pool.
Config.Treatment — the cure progress bar
| Field | Default | What it does |
|---|---|---|
defaultTimeMs | 5000 | Duration used when a catalog cure declares no timeMs of its own. |
label | 'treatment.label.treating' | Text on the progress bar. A locale key, or plain text to show it as written. |
anim | mp_suicide / pill, flag 49 | Animation played while treating. |
The Trigger Layer — Config.Triggers
Five independent systems, each switchable on its own.
NeedsSickness
| Field | Default |
|---|---|
enabled | true |
Rolls Config.NeedsSicknessRules (below) whenever needs are evaluated, scaled by immunity, and moves the immune stat up or down.
NeedsPolling
How hunger, thirst and stress reach the two needs-driven triggers when oxide-needs is not the provider.
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch for the poll. |
intervalMs | 30000 | How often needs are read per online player. Dehydration syncs on every read. |
sicknessEveryNTicks | 4 | Roll the sickness rules every Nth read — not every read. |
Why the second interval exists. Each sickness roll is a chance and moves the immune stat, so it has to run at roughly the cadence the rules were tuned for. oxide-needs fires its decay event once per game hour — about two real minutes at the default 30× time scale — and 4 × 30s matches that. If you shorten intervalMs, raise sicknessEveryNTicks to keep the product near two minutes, or players will fall ill far more often than intended.
The poll is skipped entirely while oxide-needs is running, and refuses to run if o-link has no real needs provider at all, so a server without any needs system doesn't read as permanently starving.
Dehydration
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
thirstLow | 30 | Dehydration sets in at or below this thirst. |
thirstRecover | 40 | It clears at or above this thirst. |
The gap between the two is deliberate hysteresis, so it doesn't flicker on and off at the boundary. This is a direct mirror, not a chance roll — there is no item cure and no duration.
DiseaseProgression
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
checkIntervalMs | 30000 | How often active diseases are checked against their stage timings. |
Severity only ever rises. A disease never gets better on its own.
Contagion
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
intervalMs | 5000 | How often an infectious player's client nominates nearby players. |
reportRateLimitMs | 4000 | Server-side floor on how often it accepts those reports. |
radius | 3.0 m | How close counts as exposure. The server re-checks this with half a metre of slack. |
baseChance | 0.25 | Per-check infection chance, before masks and immunity. A disease can override it with its own infectChance. |
maskReduction | 0.25 | Multiplier applied per masked party. Either side masked → a quarter of the chance; both → a sixteenth. |
At most 24 nominated targets are processed per report.
Sources.WoundInfection
A dirty injury turning septic.
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
classes | CUTTING, WILDLIFE, OTHER | Which damage classes can infect. OTHER is falls and water cannon. |
chance | 0.25 | Base chance, multiplied by susceptibility. |
disease | 'infection' | What they catch. |
Matches both fractures and bleeding wounds. One hit that raises both only rolls once — there is a one-second debounce per player so a mauling doesn't get two chances at the same infection.
Sources.RainCold
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
intervalMs | 20000 | How often an outdoors player in the rain is checked. |
chance | 0.15 | Base chance, multiplied by susceptibility. |
disease | 'flu' | What they catch. |
rainWeather | RAIN, THUNDER, CLEARING | Which weather types count as rain. |
The client confirms both the weather and that the player is outdoors using the game's own natives, so there is no dependency on any weather resource.
Forensics
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
rateLimitMs | 5000 | Minimum gap between blood-evidence emissions per player. |
environmentalClasses | OTHER, FIRE | Which fracture classes emit blood evidence. |
Bleeding wounds emit as well, but only from CUTTING and WILDLIFE. Firearm blood is deliberately excluded — a police resource drops that itself from the weapon-damage event, and emitting here too would create the evidence twice.
Catalogs
Fractures — shared/config/fractures.lua
Each entry keys a fracture type to a body part, a default severity, its physical effects and its cure.
broken_leg_left = {
label = 'fracture.broken_leg_left',
bodypart = 'LLEG',
defaultSeverity = Medical.Severity.SEVERE,
effects = { limp = true, aimSway = false, movementPenalty = 0.35 },
treatment = { item = 'medical_splint', byEmsOnly = true, timeMs = 15000 },
},| Field | Meaning |
|---|---|
label | The name shown for this fracture. A locale key, or plain text to show it as written. |
bodypart | One of the fifteen body parts. Injuries at that body part resolve to this fracture. |
defaultSeverity | 1 minor, 2 moderate, 3 severe, 4 critical. |
effects.limp | Swaps the movement animation. |
effects.aimSway | Shakes the camera while aiming or shooting. |
effects.movementPenalty | 0–1 slowdown. Capped at 0.5 in practice; the worst active fracture wins. |
treatment.item | Cure item, or nil for "no item can fix this". |
treatment.byEmsOnly | true refuses self-treatment and leaves it to the EMS pipeline. |
treatment.timeMs | Progress bar duration. |
The thirteen shipped fractures and their settings:
| Type | Body part | Severity | Limp | Aim sway | Slow | Item | Medic only |
|---|---|---|---|---|---|---|---|
broken_arm_left / _right | LARM / RARM | Moderate | — | yes | 0.05 | splint | no |
broken_hand_left / _right | LHAND / RHAND | Minor | — | yes | — | splint | no |
broken_leg_left / _right | LLEG / RLEG | Severe | yes | — | 0.35 | splint | yes |
broken_foot_left / _right | LFOOT / RFOOT | Moderate | yes | — | 0.20 | splint | no |
fractured_ribs | UPPER_BODY | Moderate | — | yes | 0.10 | — | yes |
broken_spine | SPINE | Critical | yes | yes | 0.60 | — | yes |
cracked_skull | HEAD | Critical | — | yes | 0.15 | — | yes |
fractured_neck | NECK | Severe | — | yes | 0.25 | — | yes |
fractured_pelvis | LOWER_BODY | Severe | yes | — | 0.40 | — | yes |
Wounds — shared/config/woundtypes.lua
| Type | Bleeds | Item | Medic only | Time |
|---|---|---|---|---|
gunshot | yes | medical_bandage | no | 8000 ms |
laceration | yes | medical_bandage | no | 6000 ms |
retained_bullet | no | — | yes | 20000 ms |
Config.WoundRules in the same file decides which class produces which wound, and the retained-bullet roll:
| Class | Wound | Retained-bullet chance | Retained-bullet severity |
|---|---|---|---|
SMALL_CALIBER | gunshot | 25% | Moderate |
MEDIUM_CALIBER | gunshot | 40% | Severe |
HIGH_CALIBER | gunshot | 55% | Critical |
SHOTGUN | gunshot | 35% | Critical |
CUTTING | laceration | — | — |
WILDLIFE | laceration | — | — |
Diseases — shared/config/diseases.lua
flu = {
label = 'disease.flu',
contagious = true,
stages = {
{ afterSec = 0, severity = Medical.Severity.MINOR, symptoms = { 'cough', 'fatigue' } },
{ afterSec = 600, severity = Medical.Severity.MODERATE, symptoms = { 'cough', 'fever', 'aches' } },
{ afterSec = 1800, severity = Medical.Severity.SEVERE, symptoms = { 'fever', 'aches', 'weakness' } },
},
cure = { item = 'antiviral', byEmsOnly = false },
immunitySec = 86400,
},afterSec is real seconds since the disease was contracted, ascending. immunitySec is how long after a cure the player cannot catch it again — 0 means no grace period. A disease may also carry its own infectChance to override the global contagion chance.
| Disease | Contagious | Stage timings | Cure | Medic only | Immunity |
|---|---|---|---|---|---|
flu | yes | 0 / 10 min / 30 min | antiviral | no | 24 h |
infection | no | 0 / 15 min / 45 min / 90 min | antibiotics | no | none |
pneumonia | yes | 0 / 20 min / 60 min | antibiotics | yes | 12 h |
hepatitis | yes | 0 / 60 min / 3 h | antiviral | yes | none |
Sicknesses — shared/config/sickness.lua
| Sickness | Duration | Cure | Medic only | Symptoms |
|---|---|---|---|---|
nausea | 300 s | antacid | no | queasy, screen sway |
fever | 600 s | painkillers | no | sweating, fatigue |
dizziness | 240 s | — | no | screen sway, blurred vision |
dehydration | none | — | no | fatigue, dizziness, dry mouth |
food_poisoning | 540 s | antacid | no | queasy, cramps, fatigue |
cardiac_arrest | none | — | yes | none |
dehydration and cardiac_arrest deliberately have no duration and no item cure — one mirrors the thirst stat, the other is owned by the vitals engine.
Config.NeedsSicknessRules
Which sustained needs make a player ill:
| Need | Condition | Sickness | Base chance |
|---|---|---|---|
hunger | at or below 15 | nausea | 40% |
stress | at or above 80 | dizziness | 35% |
Final chance is baseChance × (100 - immune) / 100. Any rule hitting also erodes immunity by needsHitPenalty; none hitting regenerates it by regenPerTick.
Thirst is deliberately absent — dehydration is a deterministic mirror, not a roll.
ESX note: ESX has no stress stat, so o-link reports it as 0 there and the stress rule never fires.
Symptoms — shared/config/symptoms.lua
Maps symptom tokens emitted by diseases and sicknesses onto client effects. Tokens with no entry here are silently ignored, so you can add symptoms to a catalog entry without touching code and wire the effect later.
Three kinds:
| Kind | Fields | Effect |
|---|---|---|
shake | amplitude | Continuous camera shake. Several active shakes use the strongest, not the sum. |
anim | dict, clip, intervalMs, durationMs | Plays an upper-body animation periodically. |
scenario | scenario, intervalMs, durationMs | Plays an in-place scenario periodically. Briefly locks the player. |
Shipped: cough (animation, every 22 s), nausea (vomit scenario, every 38 s), and four shakes — queasy 0.18, screen_sway 0.22, dizziness 0.28, blurred_vision 0.20.
Config.SymptomShake ('DRUNK_SHAKE') names the shake style.
Wound detection — shared/config/wounds.lua
The lowest-level tuning, ported from qbx_medical.
| Table | What it holds |
|---|---|
Config.Wounds.WeaponClasses | 96 weapon and damage-source hashes mapped to one of twelve classes |
Config.Wounds.BoneMap | 56 ped bone ids mapped to the fifteen body parts |
Config.Wounds.FractureRules | Which classes fracture, and how severity scales |
FractureRules
| Field | Default |
|---|---|
classesThatFracture | HIGH_CALIBER, SHOTGUN, HEAVY_IMPACT, EXPLOSIVE, LIGHT_IMPACT, WILDLIFE, OTHER |
alwaysFractureClasses | HEAVY_IMPACT, EXPLOSIVE, HIGH_CALIBER — these break something on every hit, regardless of damage |
severityByDamage | 60+ critical, 40+ severe, 20+ moderate, otherwise minor |
ignoredBodyParts | LFINGER, RFINGER, NONE — too small to model |
The twelve classes are SMALL_CALIBER, MEDIUM_CALIBER, HIGH_CALIBER, SHOTGUN, CUTTING, LIGHT_IMPACT, HEAVY_IMPACT, EXPLOSIVE, FIRE, SUFFOCATING, WILDLIFE and OTHER. NONE marks a source that should never injure — the stun gun uses it.
Editing this file: weapon hashes are written as backtick literals ([`WEAPON_PISTOL`]). Keep that form when you add entries.
Vitals — shared/config/vitals.lua
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch for the whole simulation. |
tickIntervalMs | 2000 | How often vitals are recalculated. |
Baseline | pulse 75, BP 120/80, SpO2 98 | A healthy player's readings. |
Quantize | pulse 2, BP 5, SpO2 1 | Values are rounded to these steps and only published when a rounded value changes, so idle players cost nothing. |
Blood
| Field | Default | What it does |
|---|---|---|
drainPerTierPerTick | 0.25 | Volume lost per bleeding tier per tick. A tier-3 bleed empties in about 4.5 minutes, tier 5 in about 2.5. |
regenPerTick | 0.15 | Volume recovered per tick while on your feet and not bleeding — about 22 minutes from empty. |
postReviveVolume | 60 | Volume is topped up to at least this on revive or respawn, so a patient doesn't immediately re-collapse. |
Blood volume is never saved to the database. It resets to full on load and is frozen entirely while a medic has the patient stabilized.
Pulse
| Field | Default | What it does |
|---|---|---|
painPerSeverity | 3 | BPM added per point of summed condition severity. |
painCap | 30 | Ceiling on pain-driven BPM. |
bleedTachyPerTier | 6 | Compensatory BPM per bleeding tier. |
decompensateBelow | 35 | Below this volume the body stops compensating and the pulse collapses proportionally. |
max | 220 | Hard ceiling. |
BP and SpO2
Blood pressure falls with bleeding tier and with lost volume (sysPerBleedTier 3, diaPerBleedTier 2, sysPerVolumeLost 0.9, diaPerVolumeLost 0.6).
Blood oxygen starts at baseline and is reduced by chest wounds (chestWoundPerSeverity 2 per severity point of an active UPPER_BODY wound), by respiratory conditions (conditionPenalties: pneumonia 4, flu 1, infection 1, per severity point), and by hypotension below lowBpBelowSystolic 90. It floors at min 60 while alive, and decays by arrestDecayPerTick 3 during arrest.
Arrest
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
pulseThreshold | 20 | A pulse at or below this counts toward arrest. |
graceTicks | 3 | Consecutive ticks below the threshold before arrest fires — six seconds at the default tick. |
conditionType | 'cardiac_arrest' | Which sickness is added. |
Arrest cannot fire while a patient is stabilized. It is cleared on revive or respawn, and a stale one is swept if a player loads in up and walking after a crash.
Notifications — shared/config/notifications.lua
| Field | Default | What it does |
|---|---|---|
enabled | true | Master switch. |
type | 'inform' | Notification style. |
minGapMs | 8000 | Minimum spacing between any two medical notifications for one player. |
periodicIntervalMs | 90000 | How often a lingering condition nudges the player again. |
The rest of the file is message pools:
fractureGeneric,illnessGeneric,woundGeneric— fallbacks.bodyparts— a pool per body region, used for fractures and sometimes for wounds. These never say "broken" or "fracture" — that is the point.symptoms— a pool per symptom token, used for diseases and sicknesses.
A fracture draws from its body region's pool. A wound draws from the bleeding pool, with a 40% chance of using the body-region pool instead. An illness draws from the pools of all its current symptoms.
Each entry in a pool is a locale key, and the wording sits in locales/en.json under notify.vague.*. Adding a line means adding a key to both files — or, if you would rather not touch the locale file, writing the sentence straight into the pool, which is shown as written:
fractureGeneric = {
'notify.vague.fracture_generic.1', -- from locales/en.json
'Something in you gave way.', -- shown verbatim
},Rewrite these freely. They are the entire voice of the system, and the only thing standing between a player and knowing their own diagnosis.