v0.0.5

Fresh install this version. Replace the resource folder rather than overlaying v0.0.5 onto an existing v0.0.4 tree. Database tables are now luxu_medicine_* instead of luxu_ambulancejob_*, blood settings moved under Config.blood, props live in config/props.lua, and disease tuning is a new Config.diseases block. Re-run INSTALL/main.sql (or let Config.database.auto_install create the schema) and merge the configuration diffs below before going live.

Summary

This release adds per-disease outbreak tuning, hospital-respawn inventory keep-lists, nested blood donation/transfusion settings, Spanish locale, and admin commands to spawn or clear virus zones. Staff can delete laboratories, place pharmacy shopkeeper peds, and keep selected items on E-respawn. Fatal headshots can skip last standing. Carry can be handed off to an external system through new client and server startCarry hooks.

On start, the resource renames leftover luxu_ambulancejob_* tables to luxu_medicine_* when the new name does not already exist. If both names are present, data is left untouched and a warning is printed. A fresh install avoids that mixed-schema state.

The shipped default locale is en (Config.locales.lang). Set it to pt or es if you want Portuguese or Spanish.

What's New

Features

  • Config.diseases overlay for infection chance, player spread, symptom interval, HP damage ticks, kill permission, zone spawn chance, rain (cold), and food-poisoning items. enabled = false also forces Config.features.diseases off
  • Admin outbreak commands: /ems:clearvirus [virus] and /ems:genvirus <virus> [label]
  • Hospital E-respawn inventory wipe with Config.status.dead.clear_inventory and a filtered_items keep-list (* wildcard)
  • Nested Config.blood donation and transfusion settings (replaces the old top-level blood_donation_* / blood_transfusion_* keys)
  • Fatal headshots skip last standing when Config.status.last_standing.headshot_instant_death is true
  • Spanish locale (locales/es.json); default language switched from pt to en
  • Pharmacy shopkeeper peds (ped_settings on shops) spawned locally per client
  • Laboratory delete for bosses
  • Hospital supply catalog: microscope slides and typed blood bags (O− through AB+)
  • Client and server hooks.startCarry — return false to bypass the built-in fireman's carry
  • config/props.lua (moved from shared/props.lua) plus a body-bag prop (xm_prop_body_bag)
  • Job applications collect a typed phone number; dates are formatted in the UI with Config.locales.code
  • Nearby-player menus resolve character names from the server instead of FiveM account names
  • Garage refuses to spawn when another vehicle is already in the spawn radius
  • INSTALL/main.sql and INSTALL/patches.sql are included in fxmanifest files so auto-install can load them
  • New luxu_medicine_blood_reserves table

Bug Fixes

  • Deathscreen and distress now restore after a fresh join — resource start no longer returns early before framework login
  • Carry-to-bed / OrthoScan handoff no longer undoes the patient lie-down pose
  • Bed and OrthoScan mounts wait for attach/ownership and use a 6 m proximity check
  • Duty clothing menu only opens for the configured EMS job (including off_<job>)
  • Wheelchair spawn waits for the model, then unlocks doors, starts the engine, and fills fuel
  • ESX job grades, offline player lookup, cash-as-money, and employee grade/fire paths go through the player bridge instead of QB-only players SQL
  • Pharmacy consumes prescription items before completing the purchase
  • Stabilize copy now requires last standing with no active bleeding (no longer last standing or dead)

File Changes

⚙️ Configuration & Bridge Files

These files are not encrypted. Review the changes below — you may need to manually apply updates to your server's config files.

config/config.lua

     locales = {
-        lang = "pt", -- locales/{lang}.json
+        lang = "en", -- locales/{lang}.json
         code = "en-US",
         symbol_fallback = "$",
         unit_system = "metric", -- "metric" | "imperial"
     },
     features = {
         --- Outbreaks, infections (zones, rain, food, player spread), and the tablet Diseases page.
+        --- Legacy master switch; Config.diseases.enabled=false also forces this off (see shared/init.lua).
         diseases = true,
     },

+    --[[ Diseases (outbreaks & infections) ]]
+    --- Per-disease tuning overlay for the VirusConfig catalog in shared/constants.lua.
+    --- 0 disables that vector; enabled = false disables the whole disease.
+    diseases = {
+        enabled = true,           -- master switch (false also forces features.diseases off)
+        zone_duration_days = 7,   -- how long generated zones persist (days)
+        damage_tick_seconds = 30, -- how often disease HP damage ticks (0 disables damage globally)
+
+        --- Player-to-player proximity spread (15s / 5m by default).
+        player_spread = {
+            enabled = true,
+            interval_seconds = 15,
+            distance = 5.0,
+        },
+
+        --- Keyed by virus name. Values mirror VirusConfig; only set what you want to change.
+        viruses = {
+            cold = {
+                enabled = true,
+                infection_probability = 35,
+                transmissibility = 50,
+                symptom_interval = 15,
+                damage_per_tick = 0,
+                can_kill = false,
+                spawn_probability = 0, -- rain-only, no zones
+                rain = { enabled = true },
+            },
+            food_poisoning = {
+                enabled = true,
+                infection_probability = 10,
+                transmissibility = 0,
+                symptom_interval = 120,
+                damage_per_tick = 1,
+                can_kill = false,
+                spawn_probability = 0,
+                food = {
+                    enabled = true,
+                    items = { "burger", "sandwich" },
+                },
+            },
+            covid = {
+                enabled = true,
+                infection_probability = 35,
+                transmissibility = 40,
+                symptom_interval = 15,
+                damage_per_tick = 2,
+                can_kill = false,
+                spawn_probability = 30,
+            },
+            contaminated_water = {
+                enabled = true,
+                infection_probability = 45,
+                transmissibility = 0,
+                symptom_interval = 10,
+                damage_per_tick = 2,
+                can_kill = false,
+                spawn_probability = 50,
+            },
+            tetanus = {
+                enabled = true,
+                infection_probability = 30,
+                transmissibility = 0,
+                symptom_interval = 10,
+                damage_per_tick = 3,
+                can_kill = true,
+                spawn_probability = 25,
+            },
+            radiation = {
+                enabled = true,
+                infection_probability = 12,
+                transmissibility = 0,
+                symptom_interval = 10,
+                damage_per_tick = 5,
+                can_kill = true,
+                spawn_probability = 10,
+            },
+        },
+    },
         --- Hospital stash shop catalog. `name` is a Config.items key or a raw item name.
-        --- Orders spend society money and add stock to luxu_ambulancejob_hospitals.inventory.
+        --- Orders spend society money and add stock to luxu_medicine_hospitals.inventory.
         hospital_supplies = {
             { name = "bandage", label = "Bandage", category = "Consumables", unitPrice = 12, reorderAt = 30 },
             { name = "medical_kit", label = "Medical Bag", category = "Equipment", unitPrice = 85, reorderAt = 10 },
             { name = "adrenaline", label = "Epinephrine Syringe", category = "Medication", unitPrice = 55, reorderAt = 15 },
             { name = "oxymeter", label = "Pulse Oximeter", category = "Equipment", unitPrice = 35, reorderAt = 10 },
             { name = "thermometer", label = "Thermometer", category = "Equipment", unitPrice = 40, reorderAt = 10 },
             { name = "syringe", label = "Syringe", category = "Consumables", unitPrice = 5, reorderAt = 40 },
+            { name = "microscope_slide", label = "Microscope Slide", category = "Consumables", unitPrice = 8, reorderAt = 20 },
             { name = "blood_bag_empty", label = "Empty Blood Bag", category = "Fluids", unitPrice = 22, reorderAt = 20 },
+            { name = "blood_bag_o-", label = "Blood Bag O-", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_o+", label = "Blood Bag O+", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_a-", label = "Blood Bag A-", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_a+", label = "Blood Bag A+", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_b-", label = "Blood Bag B-", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_b+", label = "Blood Bag B+", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_ab-", label = "Blood Bag AB-", category = "Fluids", unitPrice = 45, reorderAt = 10 },
+            { name = "blood_bag_ab+", label = "Blood Bag AB+", category = "Fluids", unitPrice = 45, reorderAt = 10 },
             { name = "defib", label = "Defibrillator", category = "Equipment", unitPrice = 120, reorderAt = 6 },
     hospital_respawn_hold_duration = 3000,
     condition_damage_interval = 5000,
-    blood_donation_minimum = 60,
-    blood_donation_amount = 20,
-    blood_donation_consent_timeout = 15000, -- ms the patient has to accept/deny
-    blood_donation_duration = 15000,        -- ms; non-cancelable extraction progress bar
-    -- One transfusion unit. Low (31–59) needs 1; Critical (≤30) needs 2.
-    blood_transfusion_amount = 30,
-    blood_transfusion_duration = 15000, -- ms; server timer + patient progress bar
-    -- HP restored to the patient when a transfusion completes (GTA scale: 200 = full).
-    blood_transfusion_health = 20,
     health_insurance_hours_renewal = 1,

+    --[[ Blood donation & transfusion ]]
+    blood = {
+        donation = {
+            minimum = 60,            -- donor needs at least this blood level (0-100)
+            amount = 20,             -- blood level taken per donation
+            consent_timeout = 15000, -- ms the patient has to accept/deny
+            duration = 15000,        -- ms; non-cancelable extraction progress bar
+        },
+        transfusion = {
+            -- One unit. Low (31–59) needs 1; Critical (≤30) needs 2.
+            amount = 30,
+            duration = 15000, -- ms; server timer + patient progress bar
+            -- HP restored when a transfusion completes (GTA scale: 200 = full). 0 disables healing.
+            health = 20,
+        },
+    },
     status = {
         last_standing = {
             invincible_duration = 10000,   -- This can't be set higher than 5000
             duration = 10 * 60 * 1000,
             instant_death_chance = 0.10,   -- 0.0–1.0; 0 disables skip to dead
+            headshot_instant_death = true, -- fatal headshots skip last standing, go straight to dead
             -- On-foot last standing uses rpemotes-style move_crawl anims. false = static writhe.
             crawl = true,
         },
+        dead = {
+            -- Hospital E-respawn inventory wipe. false keeps everything and ignores filtered_items.
+            clear_inventory = true,
+            -- Keep-list used only when clear_inventory is true. Empty = wipe every item.
+            -- Match is against the inventory item name.
+            -- `*` is the only wildcard (any run of characters, including none). Other characters are literal.
+            -- Examples: "phone*" keeps phone and phone1; "*card" keeps idcard; "id_*" keeps id_card.
+            filtered_items = {
+                -- "clothing_*",
+                -- "id_card",
+            },
+        },
         ["revive"] = {
             enabled = true,
             name = "revive",
             description = "Revive Player",
             params = {
                 { name = "target", help = "Server Id of the targeted player", optional = true }
             },
             restricted = { "group.admin", "group.mod" }
         },
+        ["clearvirus"] = {
+            enabled = true,
+            name = "ems:clearvirus",
+            description = "Clear active virus outbreak zones",
+            params = {
+                { name = "virus", help = "Virus name (empty clears all outbreaks)", optional = true }
+            },
+            restricted = { "group.admin" }
+        },
+        ["genvirus"] = {
+            enabled = true,
+            name = "ems:genvirus",
+            description = "Spawn a virus outbreak zone at your position",
+            params = {
+                { name = "virus", help = "Virus name (e.g. covid, radiation)" },
+                { name = "label", help = "Zone label", optional = true }
+            },
+            restricted = { "group.admin" }
+        },
     },
 }

config/hooks/client.lua

 function hooks.sendDistressSignal()
     return true
 end

+--- Called before the built-in carry starts on the transporter side.
+--- Return false to bypass it so an external carry system can take over
+--- (e.g. call your own export here and return false).
+---@param targetServerId number
+---@return boolean
+function hooks.startCarry(targetServerId)
+    return true
+end
+
 return hooks

config/hooks/server.lua

 --AddEventHandler("luxu_medicine:server:player_healed", function(targetSource) end)
 --AddEventHandler("luxu_medicine:server:player_injured", function(targetSource) end)

+---@class ServerHooks
 local hooks = {}
---[[ Todo ]]
+
+--- Called before a built-in carry session is created.
+--- Return false to bypass it so an external carry system can take over.
+---@param source number carrier server id
+---@param targetSource number carried server id
+---@return boolean
+function hooks.startCarry(source, targetSource)
+    return true
+end

 return hooks

config/props.lua (moved from shared/props.lua)

             attach = {
-                x = 0.39,
-                y = -0.06,
-                z = -0.06,
-                rotx = -100.0,
-                roty = -180.0,
-                rotz = -78.0,
+                x = 0.4131,
+                y = 0.0465,
+                z = 0.0815,
+                rotx = -66.9871,
+                roty = -142.5557,
+                rotz = -41.8048,
             },
         },
     },
+
+    --[[ THIS MIGHT CHANGE IN THE FUTURE ]]
     stretchers = {
-    lucas2 = {
+    lucas = {
         model = "lucas2",
         bone = PEDBONES.SKEL_ROOT,
         offsets = {
             position = vector3(0, 0, 0),
             rotation = vector3(0, 0, 0),
         },
         target = {}
+    },
+    body_bag = {
+        model = "xm_prop_body_bag"
     }
 }

fxmanifest.lua

 author "luxu-gg <[email protected]>"
 description "Luxu Medicine - Ambulance Job"
-version "0.0.4"
+version "0.0.5"

 dependencies {
     "oxmysql",
     "ox_lib",
     "/server:25770"
 }

 shared_scripts {
     "@ox_lib/init.lua",
-    "shared/**/*.lua"
+    "shared/**/*.lua",
+    "config/props.lua"
 }
 files {
     "web/**/*",
     "config/config.lua",
     "config/emergency_codes.lua",
     "config/clothes.lua",
     "config/hooks/client.lua",
     "locales/*.json",
+    "INSTALL/main.sql",
+    "INSTALL/patches.sql",
     "data/*.json",
     "bridge/framework.lua",
     "bridge/client/*.lua",
 }

INSTALL/main.sql

All product tables were renamed luxu_ambulancejob_* → luxu_medicine_* (applications, blood_donations, citizen_tags, conditions, complaints, insurances, medical_records, notes, patient_consultations, stats, tags, treatment_history, health_records, treatment_logs, hospitals, virus_zones, dispatch_history). Example:

-CREATE TABLE IF NOT EXISTS `luxu_ambulancejob_applications` (
+CREATE TABLE IF NOT EXISTS `luxu_medicine_applications` (

New table:

+CREATE TABLE IF NOT EXISTS `luxu_medicine_blood_reserves` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `char_id` varchar(255) NOT NULL,
+  `amount` int(11) NOT NULL DEFAULT 0,
+  `donated_at` datetime NOT NULL DEFAULT current_timestamp(),
+  PRIMARY KEY (`id`),
+  KEY `idx_char_id` (`char_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

INSTALL/patches.sql uses the same luxu_medicine_* names. On resource start, leftover luxu_ambulancejob_* tables are renamed when the new name is free.


Full list of files changed

Change File
Added INSTALL/items/images/bodybag.png
Modified INSTALL/main.sql
Modified INSTALL/patches.sql
Modified bridge/client/events.lua
Modified bridge/client/target.lua
Modified bridge/server/events.lua
Modified bridge/server/inventory.lua
Modified bridge/server/player.lua
Modified client/cl_main.lua
Modified client/cl_utils.lua
Modified client/handlers/bootstrap.lua
Modified client/handlers/markers.lua
Modified client/handlers/nui.lua
Added client/handlers/shop_peds.lua
Modified client/handlers/targets.lua
Modified client/handlers/virus_zones.lua
Modified client/modules/conditions/blackout_effects.lua
Modified client/modules/conditions/movement_effects.lua
Modified client/modules/conditions/viruses.lua
Modified client/modules/damage.lua
Modified client/modules/experiments/patient_overlay.lua
Modified client/modules/garage.lua
Modified client/modules/items/medical_bag.lua
Modified client/modules/minigames.lua
Modified client/modules/mobility.lua
Modified client/modules/status.lua
Modified client/modules/treatment/blood_transfusion.lua
Modified client/modules/treatment/carry.lua
Modified client/modules/treatment/controls.lua
Modified client/modules/treatment/hospital_beds.lua
Modified client/modules/treatment/hospital_orthoscan.lua
Modified client/modules/treatment/oxymeter.lua
Modified client/modules/treatment/patient_hotbar.lua
Modified client/modules/treatment/stretcher.lua
Modified client/modules/treatment/triage.lua
Modified config/config.lua
Modified config/hooks/client.lua
Modified config/hooks/server.lua
Moved shared/props.lua → config/props.lua
Modified data/shops.json
Modified fxmanifest.lua
Modified locales/en.json
Added locales/es.json
Modified locales/pt.json
Modified server/database_patches.lua
Modified server/features/applications.lua
Modified server/features/citizens.lua
Modified server/features/complaints.lua
Modified server/features/employees.lua
Modified server/features/health_records.lua
Modified server/features/laboratories.lua
Modified server/features/player.lua
Modified server/features/shops.lua
Modified server/features/treatment.lua
Modified server/features/treatment_logs.lua
Modified server/modules/blood_transfusion.lua
Modified server/modules/care.lua
Modified server/modules/conditions.lua
Modified server/modules/hospital_inventory.lua
Modified server/modules/job.lua
Modified server/modules/medical_bag.lua
Modified server/modules/minigames.lua
Modified server/modules/mobility.lua
Modified server/modules/shop.lua
Modified server/modules/statistics.lua
Modified server/modules/viruses.lua
Modified server/services/database.lua
Modified server/sv_main.lua
Modified server/sv_status.lua
Modified server/sv_utils.lua
Added shared/diseases.lua
Modified shared/init.lua
Modified shared/types.lua
Modified web/