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.

FileHolds
shared/config.luaSystem toggles: immunity, damage over time, blood decals, treatment, and the trigger layer
shared/config/wounds.luaWeapon-to-class map, bone-to-body-part map, fracture rules
shared/config/woundtypes.luaThe wound catalog and the class-to-wound rules
shared/config/fractures.luaThe fracture catalog
shared/config/diseases.luaThe disease catalog
shared/config/sickness.luaThe sickness catalog and the needs-sickness rules
shared/config/symptoms.luaWhat each symptom token does on screen
shared/config/vitals.luaThe vitals and cardiac arrest model
shared/config/notifications.luaEvery vague message a player can receive
locales/en.jsonEvery 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 needed

That 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

SettingDefaultWhat it does
Config.DebugfalsePrints diagnostic lines to the console.
Config.AssignBloodTypeOnFirstLoadtrueAssign 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.WoundReportRateLimitMs1500Minimum gap between accepted wound reports per player. Caps how fast one player can register new injuries.
Config.ExpiryCheckIntervalMs30000How 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.

FieldDefaultWhat it does
start100What a new character starts at.
min / max0 / 100Clamp range.
needsHitPenalty3Immunity lost per evaluation while a need is in the danger zone.
regenPerTick2Immunity regained per evaluation while every need is healthy.
minSusceptibility0.3Floor 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.

FieldDefaultWhat it does
enabledtrueMaster switch.
tickIntervalMs5000How often damage is applied.
minHealth0The 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:

CategoryMinor (1)Moderate (2)Severe (3)Critical (4)
Fracture1
Disease13
Sickness
Wound123

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

FieldDefaultWhat it does
MinTier2Minimum bleeding tier before decals drop.
IntervalMs4000Minimum gap between decals.
LifetimeSec60How 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

FieldDefaultWhat it does
defaultTimeMs5000Duration 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.
animmp_suicide / pill, flag 49Animation played while treating.

The Trigger Layer — Config.Triggers

Five independent systems, each switchable on its own.

NeedsSickness

FieldDefault
enabledtrue

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.

FieldDefaultWhat it does
enabledtrueMaster switch for the poll.
intervalMs30000How often needs are read per online player. Dehydration syncs on every read.
sicknessEveryNTicks4Roll 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

FieldDefaultWhat it does
enabledtrueMaster switch.
thirstLow30Dehydration sets in at or below this thirst.
thirstRecover40It 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

FieldDefaultWhat it does
enabledtrueMaster switch.
checkIntervalMs30000How often active diseases are checked against their stage timings.

Severity only ever rises. A disease never gets better on its own.

Contagion

FieldDefaultWhat it does
enabledtrueMaster switch.
intervalMs5000How often an infectious player's client nominates nearby players.
reportRateLimitMs4000Server-side floor on how often it accepts those reports.
radius3.0 mHow close counts as exposure. The server re-checks this with half a metre of slack.
baseChance0.25Per-check infection chance, before masks and immunity. A disease can override it with its own infectChance.
maskReduction0.25Multiplier 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.

FieldDefaultWhat it does
enabledtrueMaster switch.
classesCUTTING, WILDLIFE, OTHERWhich damage classes can infect. OTHER is falls and water cannon.
chance0.25Base 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

FieldDefaultWhat it does
enabledtrueMaster switch.
intervalMs20000How often an outdoors player in the rain is checked.
chance0.15Base chance, multiplied by susceptibility.
disease'flu'What they catch.
rainWeatherRAIN, THUNDER, CLEARINGWhich 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

FieldDefaultWhat it does
enabledtrueMaster switch.
rateLimitMs5000Minimum gap between blood-evidence emissions per player.
environmentalClassesOTHER, FIREWhich 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 },
},
FieldMeaning
labelThe name shown for this fracture. A locale key, or plain text to show it as written.
bodypartOne of the fifteen body parts. Injuries at that body part resolve to this fracture.
defaultSeverity1 minor, 2 moderate, 3 severe, 4 critical.
effects.limpSwaps the movement animation.
effects.aimSwayShakes the camera while aiming or shooting.
effects.movementPenalty0–1 slowdown. Capped at 0.5 in practice; the worst active fracture wins.
treatment.itemCure item, or nil for "no item can fix this".
treatment.byEmsOnlytrue refuses self-treatment and leaves it to the EMS pipeline.
treatment.timeMsProgress bar duration.

The thirteen shipped fractures and their settings:

TypeBody partSeverityLimpAim swaySlowItemMedic only
broken_arm_left / _rightLARM / RARMModerateyes0.05splintno
broken_hand_left / _rightLHAND / RHANDMinoryessplintno
broken_leg_left / _rightLLEG / RLEGSevereyes0.35splintyes
broken_foot_left / _rightLFOOT / RFOOTModerateyes0.20splintno
fractured_ribsUPPER_BODYModerateyes0.10yes
broken_spineSPINECriticalyesyes0.60yes
cracked_skullHEADCriticalyes0.15yes
fractured_neckNECKSevereyes0.25yes
fractured_pelvisLOWER_BODYSevereyes0.40yes

Wounds — shared/config/woundtypes.lua

TypeBleedsItemMedic onlyTime
gunshotyesmedical_bandageno8000 ms
lacerationyesmedical_bandageno6000 ms
retained_bulletnoyes20000 ms

Config.WoundRules in the same file decides which class produces which wound, and the retained-bullet roll:

ClassWoundRetained-bullet chanceRetained-bullet severity
SMALL_CALIBERgunshot25%Moderate
MEDIUM_CALIBERgunshot40%Severe
HIGH_CALIBERgunshot55%Critical
SHOTGUNgunshot35%Critical
CUTTINGlaceration
WILDLIFElaceration

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.

DiseaseContagiousStage timingsCureMedic onlyImmunity
fluyes0 / 10 min / 30 minantiviralno24 h
infectionno0 / 15 min / 45 min / 90 minantibioticsnonone
pneumoniayes0 / 20 min / 60 minantibioticsyes12 h
hepatitisyes0 / 60 min / 3 hantiviralyesnone

Sicknesses — shared/config/sickness.lua

SicknessDurationCureMedic onlySymptoms
nausea300 santacidnoqueasy, screen sway
fever600 spainkillersnosweating, fatigue
dizziness240 snoscreen sway, blurred vision
dehydrationnonenofatigue, dizziness, dry mouth
food_poisoning540 santacidnoqueasy, cramps, fatigue
cardiac_arrestnoneyesnone

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:

NeedConditionSicknessBase chance
hungerat or below 15nausea40%
stressat or above 80dizziness35%

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:

KindFieldsEffect
shakeamplitudeContinuous camera shake. Several active shakes use the strongest, not the sum.
animdict, clip, intervalMs, durationMsPlays an upper-body animation periodically.
scenarioscenario, intervalMs, durationMsPlays 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.

TableWhat it holds
Config.Wounds.WeaponClasses96 weapon and damage-source hashes mapped to one of twelve classes
Config.Wounds.BoneMap56 ped bone ids mapped to the fifteen body parts
Config.Wounds.FractureRulesWhich classes fracture, and how severity scales

FractureRules

FieldDefault
classesThatFractureHIGH_CALIBER, SHOTGUN, HEAVY_IMPACT, EXPLOSIVE, LIGHT_IMPACT, WILDLIFE, OTHER
alwaysFractureClassesHEAVY_IMPACT, EXPLOSIVE, HIGH_CALIBER — these break something on every hit, regardless of damage
severityByDamage60+ critical, 40+ severe, 20+ moderate, otherwise minor
ignoredBodyPartsLFINGER, 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

FieldDefaultWhat it does
enabledtrueMaster switch for the whole simulation.
tickIntervalMs2000How often vitals are recalculated.
Baselinepulse 75, BP 120/80, SpO2 98A healthy player's readings.
Quantizepulse 2, BP 5, SpO2 1Values are rounded to these steps and only published when a rounded value changes, so idle players cost nothing.

Blood

FieldDefaultWhat it does
drainPerTierPerTick0.25Volume lost per bleeding tier per tick. A tier-3 bleed empties in about 4.5 minutes, tier 5 in about 2.5.
regenPerTick0.15Volume recovered per tick while on your feet and not bleeding — about 22 minutes from empty.
postReviveVolume60Volume 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

FieldDefaultWhat it does
painPerSeverity3BPM added per point of summed condition severity.
painCap30Ceiling on pain-driven BPM.
bleedTachyPerTier6Compensatory BPM per bleeding tier.
decompensateBelow35Below this volume the body stops compensating and the pulse collapses proportionally.
max220Hard 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

FieldDefaultWhat it does
enabledtrueMaster switch.
pulseThreshold20A pulse at or below this counts toward arrest.
graceTicks3Consecutive 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

FieldDefaultWhat it does
enabledtrueMaster switch.
type'inform'Notification style.
minGapMs8000Minimum spacing between any two medical notifications for one player.
periodicIntervalMs90000How 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.