# Developer > Developer API reference for ML HealthSystem Category: HEALTH SYSTEM · Source: https://miciomods.it/docs/ml-healthsystem-developer · Last updated: 2026-10-05 ## Overview Server and client exports plus open event hooks for integrating with ML HealthSystem. The editable event handlers and connectors are in the `open/` folder: `open/server/handlers.lua`, `open/client/handlers.lua`, and the `radiation_connector.lua` files. These are escrow-ignored (plain source) and can be edited without touching the core system. Live medical state (death state, diseases, injuries, life points) replicates to clients in real time. Persistence uses three database tables: `ml_healthsystem` (per-identifier JSON health blob), `ml_lifepoints` (life points), `ml_health_tents` (placed respawn tents). All three are created automatically on first start. > **INFO:** Event toggles > > The death, last stand, and respawn events only fire when enabled in `shared/config/open.lua` (`Config.OpenHandlers`). All three default to `true`. ## Server Exports ### Disease Management ```lua exports.ml_healthsystem:AddDisease(source, diseaseName) ``` - `AddDisease(source, diseaseName)`: Infects a player with a disease defined in `Config.Diseases`. Returns `true` on success, `false` if the disease key does not exist or is already active. ```lua exports.ml_healthsystem:CureDiseaseForce(source, diseaseName) ``` - `CureDiseaseForce(source, diseaseName)`: Force-cures a specific disease, bypassing `maxCurableStage` checks. Returns `true` if cured. ```lua exports.ml_healthsystem:CureAllDiseases(source) ``` - `CureAllDiseases(source)`: Removes all active diseases from a player. > **INFO:** Disease keys > > Pass the disease key, not the label. The six shipped defaults are `influenza`, `patogeno_vx_9`, `radiazioni`, `infezione`, `intossicazione`, `tetano` (defined in `shared/config/diseases.lua`). Keys change if the list is edited. ### Revive and Death ```lua local ok, reason = exports.ml_healthsystem:RevivePlayer(source, { ClearDiseases = false }) ``` - `RevivePlayer(source, settings?)`: Revives a player in place, using `Config.ReviveSettings` in `server/config_server.lua` with any fields supplied for this call. It does not run the hospital respawn flow, inventory wipe, or Life Points penalties. - `source`: An online player's positive integer server ID. A numeric string is also accepted. Alive, dead, and last-stand players are accepted. Returns `true` after the server reset completes and the client revive event is sent. This does not confirm that the client animation has finished. Invalid or offline targets return `false, 'invalid_player'`. Unknown fields, non-boolean values, or a non-table settings argument return `false, 'invalid_options'`. Call without settings to use `Config.ReviveSettings` unchanged: ```lua local ok, reason = exports.ml_healthsystem:RevivePlayer(source) ``` ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, { ClearDiseases = false }) ``` `HospitalRespawn(source, settings?, hospitalId?)`: Runs the hospital respawn flow for a downed player, using `Config.HospitalReset` in `server/config_server.lua` with any per-call overrides. - `source`: An online player's positive integer server ID. A numeric string is also accepted. The player must be in `dead` or `last_stand`. #### Reset Options Reference - `settings`: Optional table for either export. Include only the boolean fields to change. Omitted fields inherit `Config.ReviveSettings` for `RevivePlayer` or `Config.HospitalReset` for `HospitalRespawn`. The global configuration and the supplied table are not modified. - `RestoreHealth`: Restore full health when `true`. When `false`, keep the pre-reset health read by the server, clamped between 101 and `Config.MaxHealth` so the player can return alive. - `RestoreBlood`: Restore blood volume when `true`; preserve the current volume when `false`. - `ResetBleeding`: Stop bleeding when `true`; preserve the current bleeding state when `false`. - `ClearInjuries`: Remove injuries when `true`; preserve the existing wounds when `false`. - `ClearFractures`: Remove fractures when `true`; preserve them when `false`. - `ClearCasts`: Remove casts when `true`; preserve them when `false`. - `ClearDiseases`: Remove diseases when `true`; preserve them when `false`. - `ResetHungerThirst`: Reset hunger, thirst, and stress when `true`; preserve them when `false`. Preserved diseases, low blood volume, and bleeding continue to apply their normal effects and damage after respawn. The options do not disable medical processing. - `hospitalId`: Optional exact key from `Config.StartSpawn` in `shared/config/respawn.lua`, not its display label. With a settings table, pass this as the third argument. A string as the second argument selects a hospital using the global reset settings. For the shipped example, the key is `prison`. Omit the key to use a random hospital when `Config.RespawnSettings.RandomSpawn` is true, otherwise the nearest hospital. With no hospital configured, the flow uses `Config.RespawnSettings.FallbackSpawn`. The export bypasses the death timer and spawn selector, sets the spawn type to `hospital`, and runs the normal teleport, configured cinematic camera, and respawn hooks. Inventory wipe and Life Points penalties still use their own global settings and are not controlled by the reset options. Returns `true` after the server reset completes and the client respawn event is sent. This does not confirm that the client's teleport or animation has finished. `HospitalRespawn` returns `false, reason` when the request is rejected or the respawn fails: - `invalid_player`: The server ID is invalid or the player is offline. - `player_alive`: The player is neither `dead` nor `last_stand`. - `invalid_hospital`: The supplied hospital key is invalid, unknown, or has no configured spawns. - `invalid_options`: The settings argument is not a table, a key is unknown, or a value is not boolean. `HospitalRespawn` also accepts a string as the second argument to select a hospital using the global reset settings. - `respawn_in_progress`: A respawn is already being processed for the player. - `respawn_failed`: The respawn encountered an internal error. ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source) ``` Call without settings to use `Config.HospitalReset` unchanged. To change a field for one respawn, supply only that field in the settings table. ```lua exports.ml_healthsystem:SetDeathSystemDisabled(source, isDisabled) ``` - `SetDeathSystemDisabled(source, isDisabled)`: Disables or re-enables the death system for one player. Use for cutscenes or safe zones. ```lua exports.ml_healthsystem:SetDeathReason(source, reason) ``` - `SetDeathReason(source, reason)`: Sets a custom death reason string (max 64 chars) consumed on the player's next death. The reason feeds `Config.LifePoints.ExternalReasons` penalty matching. Calls from outside the resource are gated to an allowlist (`ml_radiation`, `ml_climaticvitals`); foreign resources are rejected. ### Field Treatment ```lua exports.ml_healthsystem:ApplyTempBandage(source) ``` - `ApplyTempBandage(source, duration, previousBleedLevel)`: Applies a temporary bandage that stops bleeding for duration seconds, then restores the previous bleed level. ```lua exports.ml_healthsystem:AddBloodRegenBuff(source) ``` - `AddBloodRegenBuff(source, durationSeconds)`: Applies a blood regeneration boost. ### Life Points Life points only respond when `Config.LifePoints.Enabled = true` (`server/config_server.lua`). When disabled, the getters return `-1` and the mutators return `false`. ```lua exports.ml_healthsystem:GetLifePoints(source) ``` - `GetLifePoints(source)`: Returns the player's current life points (reads/creates the `ml_lifepoints` row). Returns `-1` if the system is disabled. ```lua exports.ml_healthsystem:GetLifePointsMax(source) ``` - `GetLifePointsMax()`: Returns the maximum (`Config.LifePoints.StartingPoints`). Returns `-1` if disabled. ```lua exports.ml_healthsystem:IsLifePointsEnabled() ``` - `IsLifePointsEnabled()`: Returns `true` if the life points system is active. ```lua exports.ml_healthsystem:SetLifePoints(source, value) ``` - `SetLifePoints(source, value)`: Sets a player's life points to an exact value, clamped to `[MinPoints, StartingPoints]`. Returns `true` on success. ```lua exports.ml_healthsystem:AddLifePoints(source, amount) ``` - `AddLifePoints(source, amount)`: Adds life points (clamped to max). Returns `true` on success. ```lua exports.ml_healthsystem:RemoveLifePoints(source, amount) ``` - `RemoveLifePoints(source, amount)`: Removes life points (clamped to min). Returns `true` on success. > **WARNING:** Mutator allowlist > > `SetLifePoints`, `AddLifePoints`, and `RemoveLifePoints` are gated by `CanMutateLifePoints`. Internal calls pass. External resources pass only if listed in `Config.LifePoints.ExportAllowlist`; if that list is empty, a non-staff player context is blocked. Add the calling resource to the allowlist before relying on these from another script. ### Spawn Points and Tents ```lua exports.ml_healthsystem:GetSpawnPoints() ``` - `GetSpawnPoints()`: Returns the configured spawn table (`Config.StartSpawn` from `shared/config/respawn.lua`). ```lua exports.ml_healthsystem:GetTentCoordsById(tentId) ``` - `GetTentCoordsById(tentId)`: Returns `x, y, z, heading, owner` for a placed tent from `ml_health_tents`. ### Buffs ```lua exports.ml_healthsystem:GetActiveBuffs(source) ``` - `GetActiveBuffs(source)`: Returns the active temporary buffs (regen boosts, temp bandages) for a player. ### Zombie Bites ```lua exports.ml_healthsystem:ApplyZombieBite(source, opts) ``` - `ApplyZombieBite(source, opts)`: applies the bite wound to a player and rolls the contact infection in the same call. Every `opts` field is optional: `part` (body zone, random when omitted), `damage` (defaults to `Config.ZombieBite.Damage`), `infect` (set it to `false` to skip the infection roll), `chance` (infection chance in percent for this bite) and `force` (infect without rolling). Returns `false` when the player is offline, dead or when the feature is disabled. Health damage stays with the calling script. ```lua exports.ml_healthsystem:InfectFromContact(source, opts) ``` - `InfectFromContact(source, opts)`: rolls the contact infection on its own, with no wound. `opts` accepts `disease` (defaults to `Config.ContactInfection.Disease`), `chance` and `force`. The roll always runs server-side, and it is skipped when the player already carries the disease or is not alive. **shared/config/integrations.lua** ```lua Config.ZombieBite = { Enabled = true, WeaponClass = 'ZOMBIE_BITE', Damage = 12, Infect = true, Chance = nil, Parts = { 'left_arm', 'right_arm', 'neck', 'chest', 'left_leg', 'right_leg' }, } ``` The bite registers as its own wound type, so the medic panel names it a bite instead of generic melee trauma, and dressings, suture kit, IFAK and surgical kit treat it. Bleeding, severity and balance come from the `ZOMBIE_BITE` entry in `Config.WeaponClasses` (`shared/config/weapons.lua`), and the disease from `Config.ContactInfection.Disease`. ### Zombie transformation These server exports require `Config.ZombieTransformation.Enabled = true`. The target must be a loaded player character. `playerId` is a positive integer server ID. Transforming, curing and changing a profile are restricted by `ExportAllowlist`; an empty list allows server resources. Validate your own gameplay permissions before calling them from a server event. ```lua exports.ml_healthsystem:TransformPlayer(playerId, options) exports.ml_healthsystem:CureZombie(playerId, options) exports.ml_healthsystem:SetZombieProfile(playerId, profileId, options) exports.ml_healthsystem:GetZombieState(playerId) exports.ml_healthsystem:IsZombie(playerId) exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId) ``` `TransformPlayer(playerId, options?)` requires an alive player who is not already transformed. `options` is optional: `profile?` selects a key from `Profiles`, defaulting to `DefaultProfile`; `reason?` is a string recorded with the state; `duration?` is a number of seconds from 0 to 31536000, where 0 disables expiry; `ignoreByZombies?` is a boolean that overrides the profile setting. Profile and reason strings accept at most 64 bytes. `CureZombie(playerId, options?)` requires a transformed character. Optional fields are `reason?`, a string of up to 64 bytes, and `clearDisease?`, a boolean. `clearDisease` defaults to true and removes diseases listed in `ZombieTransformation.Diseases`. With false, a disease still at its trigger stage can transform the player again. `SetZombieProfile(playerId, profileId, options?)` changes an existing transformation. `profileId` is a configured profile key. Optional fields are `reason?`, `duration?` and `ignoreByZombies?`, with the same types as `TransformPlayer`. Omitting duration keeps the original expiry, even when the new profile has a different default duration. Providing it starts a new expiry from the current server time. The transformation start time is preserved. Each mutating export returns `true, snapshot` on success or `false, error` on failure. Errors include `disabled`, `forbidden_resource`, `invalid_player`, `not_ready`, `busy`, `invalid_options`, `invalid_profile`, `already_transformed`, `not_transformed` and `not_alive`, depending on the operation. Unknown option fields are rejected. `GetZombieState(playerId)` returns a copied snapshot, or nil when the feature is disabled or the character is not loaded. `IsZombie(playerId)` returns whether the character is transformed, including while dead. `ShouldZombiesIgnorePlayer(playerId)` returns true only while the transformation is active and `ignoreByZombies` is true. Both boolean exports return false when the feature is disabled or the character is unavailable. **Zombie state snapshot** ```lua { version = 1, playerId = 42, transformed = true, active = true, profile = 'default', reason = 'infection', revision = 3, session = 1791198001, transformedAt = 1791198000, expiresAt = 0, ignoreByZombies = true, } ``` This is an example snapshot. `version` identifies its format. `playerId` is the server ID. `transformed` records the character condition; `active` is false when death suspends its effects. `profile` is the selected profile key and may be nil after recovery. `reason` records the latest change. `transformedAt` and `expiresAt` use server Unix seconds; an expiry of 0 means there is no timer. `revision` and `session` identify state changes and character sessions; treat them as opaque values. `ignoreByZombies` is the targeting preference, which only grants avoidance while active. `GetMedicalData(playerId)` also includes this snapshot in its `zombie` field. ## Client Exports ### Disease Management ```lua exports.ml_healthsystem:HasDisease(diseaseName) ``` - `HasDisease(diseaseName)`: Returns `true` if the local player has the disease (reads the `medical:diseases` live state). ```lua exports.ml_healthsystem:GetActiveDiseases() ``` - `GetActiveDiseases()`: Returns the table of active diseases on the local player. ```lua exports.ml_healthsystem:AddDisease(diseaseName) ``` - `AddDisease(diseaseName)`: Requests the server to infect the local player. > **INFO:** Server-side infect and revive > > The client `AddDisease` and `Revive` exports are staff-only. To infect or revive a player from a server resource, call `exports.ml_healthsystem:AddDisease(src, name)` and `exports.ml_healthsystem:RevivePlayer(src)`. Curing is handled by the cure item. ### Death and Revive ```lua exports.ml_healthsystem:isDead() ``` - `isDead()`: Returns `true` if the local player is dead or in last stand. ```lua exports.ml_healthsystem:Revive() ``` - `Revive()`: Requests a self-revive from the server (staff-gated server-side). ```lua exports.ml_healthsystem:SetDeathDisabled(disabled) ``` - `SetDeathDisabled(disabled)`: Sets the local `disableDeathSystem` live state. ### System State ```lua exports.ml_healthsystem:isSystemReady() ``` - `isSystemReady()`: Returns `true` once the medical system has initialized. ### Damage and Combat ```lua exports.ml_healthsystem:GetLastDamageData() ``` - `GetLastDamageData()`: Returns the last damage context: `weaponHash`, `weaponClass`, `attackerId`, `bodyPart`, `attackerModel`, `timestamp`. `weaponClass` defaults to `'UNKNOWN'` and `bodyPart` to `'chest'` when no context exists. ```lua exports.ml_healthsystem:BuildDeathData() ``` - `BuildDeathData()`: Builds a death object from the last damage event: `weaponHash`, `weaponClass`, `attackerId`, `isPlayerKill`, `bodyPart`, `cause`, `timestamp`. `cause` is derived from `weaponClass` (gunshot, explosion, stabbing, fall, burn, etc.). ```lua exports.ml_healthsystem:ApplyWound(part, amount, weaponClass) ``` - `ApplyWound(part?, amount?, weaponClass?)`: applies a body-part wound to the calling player through the full damage pipeline, so bleeding, severity and the scan panel all react. `part` is a body zone like `'left_leg'`, or `'random'` to pick one. `amount` defaults to `15`, `weaponClass` overrides the wound type. Returns `true` when a wound landed. Call it on the victim's client from an external damage source such as a zombie script. ```lua exports.ml_healthsystem:ApplyZombieBite(opts) ``` - `ApplyZombieBite(opts)`: client-side bite, for scripts that detect the attack on the victim's own client. Same options as the server export except `force`. The infection roll still happens on the server. ### Life Points (Client) ```lua exports.ml_healthsystem:GetLifePoints() ``` - `GetLifePoints()`: Returns the local player's life points from the `lifepoints` live state. Returns `-1` if disabled. ```lua exports.ml_healthsystem:GetLifePointsMax() ``` - `GetLifePointsMax()`: Returns `Config.LifePoints.StartingPoints`. Returns `-1` if disabled. ```lua exports.ml_healthsystem:IsLifePointsEnabled() ``` - `IsLifePointsEnabled()`: Returns `true` if the life points system is active. ### Tent (Client) ```lua exports.ml_healthsystem:GetPlayerTentClient() ``` - `GetPlayerTentClient()`: Returns the local player's tracked tent data on the client. ### Health Panel ```lua exports.ml_healthsystem:OpenHealthPanel() ``` - `OpenHealthPanel()`: opens the local player's health panel. Use it from a radial menu, a phone app or any other resource. The same panel answers to the client event `ml_healthsystem:client:OpenHealthPanel`, which the server can fire with `TriggerClientEvent`. **shared/config/ui.lua** ```lua Config.HealthMenu = { Command = 'health', Keybind = false, } ``` - `Command`: chat command that opens the panel. Set it to `false` to remove it. - `Keybind`: default key for that command, for example `'H'`. It goes through the FiveM key mapping system, so players can rebind or clear it in the game settings. `false` leaves it unbound. ### Zombie state and integrations ```lua exports.ml_healthsystem:GetZombieState(playerId) exports.ml_healthsystem:IsZombie(playerId) exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId) exports.ml_healthsystem:RefreshZombieStates(playerIds) exports.ml_healthsystem:GetZombieCapabilities() exports.ml_healthsystem:RegisterZombieInventoryProvider(name, provider) exports.ml_healthsystem:RegisterZombieAppearanceProvider(name, provider) ``` `GetZombieState(playerId?)`, `IsZombie(playerId?)` and `ShouldZombiesIgnorePlayer(playerId?)` read server-confirmed snapshots. Omit the positive integer server ID to read the local player. They return the same snapshot or boolean meanings as the server exports. Unknown or stale remote state returns `nil, 'not_in_scope'`; a disabled feature returns `nil, 'disabled'`. Handle these results before deciding whether an NPC can target the player. Client reads do not authorize gameplay changes. `RefreshZombieStates(playerIds)` accepts an array of visible player server IDs. It queues up to 32 IDs per call and returns true when queued, `false, 'not_loaded'` before the character loads, or `false, 'disabled'` when disabled. The response arrives asynchronously. `ml_healthsystem:client:ZombieChanged` fires when an accepted snapshot changes; refreshing an unchanged snapshot does not fire the event. Read state again during the AI's normal target update. State arriving through a ped state bag is a presentation hint, so the client API refreshes it from the server before using it. `GetZombieCapabilities()` returns `enabled`, `originalPed` and `modelSwitch`, plus `inventory` and `appearance` tables. `inventory` contains the selected `provider`, `lock` and `close` capabilities. `appearance` contains `provider`, `capture`, `restore`, `pendingRestore` and `restorationError`. Capability flags report the registered integration; they do not guarantee that another resource honors its locks or restores an appearance successfully. The local `GetMedicalData()` result also has a `zombie` field. **Inventory provider** `RegisterZombieInventoryProvider(name, provider)` takes a nonempty provider name of up to 64 bytes and a table. `SetLocked(locked, state)` is required. `Close()` and `SetWeaponsBlocked(blocked, state)` are optional. Return true after applying the requested change. Registration returns an unregister function, or false for an invalid provider. Select its name with `ZombieHandlers.GetInventoryProvider()` in `open/client/zombie_handlers.lua`. ```lua local unregister = exports.ml_healthsystem:RegisterZombieInventoryProvider('my_inventory', { SetLocked = function(locked, state) return MyInventory.SetMedicalZombieLock(locked) end, Close = function() return MyInventory.Close() end, SetWeaponsBlocked = function(blocked, state) return MyInventory.SetMedicalZombieWeaponLock(blocked) end, }) ``` `MyInventory` represents your inventory integration, so replace these calls with its actual API. Use a separate lock reason that composes with handcuffs, death and other restrictions. Keep weapons and items in the inventory. The built-in ox_inventory integration sets its lock flags and closes through ML Bridge. Other inventories need a provider when they do not honor those flags; native control blocking alone cannot close their NUI or reject their commands. **Appearance provider and optional model switch** `RegisterZombieAppearanceProvider(name, provider)` uses the same name rules and registration return values as the inventory provider. `Capture(ped)` and `Restore(ped, snapshot)` are required. `Apply(ped, profile, state)` is optional. Capture must return a complete table for the original character: model-specific appearance, face, overlays, clothes, props and any tattoos or decorations managed by your appearance system. ```lua local unregister = exports.ml_healthsystem:RegisterZombieAppearanceProvider('my_appearance', { Capture = function(ped) return MyAppearance.CaptureCompleteCharacter(ped) end, Restore = function(ped, original) return MyAppearance.RestoreCompleteCharacter(ped, original) end, Apply = function(ped, profile, state) return MyAppearance.ApplyZombiePresentation(ped, profile, state) end, }) ``` Replace `MyAppearance` with your own appearance API. `Restore` and `Apply` must finish synchronously, without Wait or asynchronous callbacks, and return true on success. In the selected profile, set `Appearance.Mode = 'model'`, choose a model installed on your server and set `Appearance.Provider = 'my_appearance'`. The owner changes the model; observers apply scoped presentation. A missing provider or failed capture keeps the original ped. A failed restore retains the original snapshot for retries while the same character is loaded and holds further model switches until restoration succeeds. Keep the provider running while ML Health System stops so it can restore the character. The retained snapshot is in client memory; your appearance system must provide recovery if the client or resource shuts down before a failed restore completes. Original mode requires no appearance provider. No third-party zombie model is included. ## Server Handlers Located in `open/server/handlers.lua`. The death and respawn events fire only when the matching `Config.OpenHandlers` toggle is enabled in `shared/config/open.lua`. ### Death Handler **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:onPlayerDeath', function(_clientData) if GetInvokingResource() then return end local src = source end) ``` > **SUCCESS:** Server-authoritative payload > > The original client payload is discarded. `deathData` is rebuilt from `PlayerDeathContext` (populated server-side with strict validation) plus the `deathState` live state. Fields `weaponHash`, `weaponClass`, `bodyPart`, `isPlayerKill`, `attackerId`, `attackerModel`, `deathReason`, `deathZone`, `cause`, and `coords` are all server-validated and safe for authoritative actions (permadeath, money, database logging). The event is rate-limited and rejects foreign-resource triggers. ### Respawn Handler **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:onPlayerRespawned', function(_clientCoords, _clientSpawnType) if GetInvokingResource() then return end local src = source end) ``` `spawnType` is validated against `hospital`, `bed`, `tent`, `random`, `last_position` (anything else becomes `'unknown'`). Coordinates are recomputed server-side from `GetEntityCoords(GetPlayerPed(src))`, so the client-sent coords cannot teleport-spoof. ### Hunger/Thirst Reset **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:ResetHungerThirst', function() end) ``` Fired automatically on revive. Permission is granted one-time per revive cycle (`reviveResetAllowed`) with a 5-second cooldown, and only applies while `deathState == 'alive'`. Uses the `ml_bridge` API through Bridge.SetHunger, Bridge.SetThirst, and Bridge.RemoveStress for cross-framework compatibility. ### Item-to-Disease Wiring `open/server/handlers.lua` also listens to `ox_inventory:usedItem` and rolls the infection chance defined in `shared/config/item_diseases.lua` (`Config.ItemDiseases`). Add a "this food makes you sick" entry there; no handler edits needed. Downed and dead players are skipped. ## Client Handlers Located in `open/client/handlers.lua`. ### Death Event This event fires when the local player dies, if Config.OpenHandlers.EnableDeathEvent is enabled. **open/client/handlers.lua** ```lua AddEventHandler('ml_healthsystem:onPlayerDeath', function(deathData) end) ``` ### Last Stand Event This event fires when the local player enters last stand, if Config.OpenHandlers.EnableLastStandEvent is enabled. **open/client/handlers.lua** ```lua AddEventHandler('ml_healthsystem:onPlayerLastStand', function(damageData) end) ``` The radiation bridge lives in `open/client/radiation_connector.lua` and `open/server/radiation_connector.lua`; both early-return unless `Config.RadiationIntegration.Enabled` and `ml_radiation` are present. The client connector is passive (debug logging only); all infect/cure logic is server-side. ### Zombie state events These are local integration events. Use AddEventHandler on the matching side. `playerId` is a server ID; `state` is the new snapshot and `previous` is the previous snapshot, when available. The server event represents the canonical state. The client event runs when the session or revision of an accepted server-confirmed snapshot changes for a visible player. ```lua AddEventHandler('ml_healthsystem:server:ZombieStateChanged', function(playerId, state, previous) end) ``` ```lua AddEventHandler('ml_healthsystem:client:ZombieChanged', function(playerId, state, previous) end) ``` ### Zombie presentation handlers Edit `open/client/zombie_handlers.lua` for custom effects or provider selection. The presentation callbacks run locally for the player ped in scope. `state` and `previous` have the snapshot format above. Keep effects reversible so they can be removed when a player recovers or leaves scope. **open/client/zombie_handlers.lua** ```lua function ZombieHandlers.OnPresentationChanged(playerId, ped, state, previous) end function ZombieHandlers.OnPresentationCleared(playerId, ped, state) end function ZombieHandlers.RestoreMovement(ped) return false end function ZombieHandlers.GetInventoryProvider() return nil end function ZombieHandlers.GetAppearanceProvider(profile) return nil end ``` `OnPresentationChanged` applies your cosmetic changes; `OnPresentationCleared` removes them. `RestoreMovement(ped)` must return true if your integration restored its own movement clipset, otherwise ML Health System resets it. `GetInventoryProvider()` returns a registered name or nil. `GetAppearanceProvider(profile)` returns a registered appearance name or nil; the profile can also select one with `Appearance.Provider`. ## Configuration The configuration is split by domain across `shared/config/*.lua` plus `server/config_server.lua`. The `shared/config.lua` file is only the index header; the real values live in the per-domain files below. **Config file map** - `core.lua`: `Config.Debug`, `Config.Locale`, `Config.Theme`, `Config.DisableHealthRegen` - `vitals.lua`: blood, bandages, armor, wound levels, blood trails, cast duration - `bodyparts.lua`: the nine body parts (armor, fracture, cast) - `diseases.lua`: diseases and stages (customer-editable) - `items.lua`: `Config.MedicalItems` (customer-editable) and `Config.XpCooldownSeconds` - `anim_presets.lua`: animation presets reused by items - `masks.lua`: protective masks (customer-editable) - `fractures.lua`: fracture model wiring - `weapons.lua`: weapon-class wound profiles, weapon→class map, body-part health thresholds - `respawn.lua`: `Config.RespawnSettings`, `Config.StartSpawn`, `Config.HrsBase`, `Config.RespawnTent` - `lifecycle.lua`: last stand and death tuning - `effects.lua`: visual effects, voice, `Config.Flare`, vehicle crash, NPC interaction - `ui.lua`: scan UI, medical jobs, commands - `integrations.lua`: radiation integration, contact infection, and `Config.LifePoints.Zones` - `item_diseases.lua`: `Config.ItemDiseases` (food/item → disease wiring) - `security.lua`: `Config.AntiBunnyHop` - `open.lua`: `Config.OpenHandlers` event toggles zombie.lua: optional player transformation, profiles, restrictions, recovery and integration settings - `server/config_server.lua`: server-only settings: Discord logging, `Config.LifePoints` (enabled, penalties, allowlist), inventory wipe, revive/hospital reset Diseases, items, and masks are shipped as defaults and are fully customer-editable: add, remove, or modify entries in their config file freely. The escrowed `internal/balance/*.lua` files (performance throttles, damage control, blood-group probabilities, weapon penalties, anti-bunny-hop sensitivity presets, body-part balance) are encrypted and cannot be edited; the editable AntiBunnyHop settings are in `security.lua`: **shared/config/security.lua** ```lua Config.AntiBunnyHop = { Enabled = true, Sensitivity = 'medium', Notify = true, Message = "Slow down! Don't jump so often.", } ``` - `Enabled`: master switch for the anti-bunny-hop ragdoll. - `Sensitivity`: selects an escrowed preset: `low` (extreme hops only), `medium` (recommended), `high` (aggressive), `extreme` (ragdoll almost always). - `Notify`: show a notification when a hop is blocked. - `Message`: notification text. ## Examples **Charge for Hospital Respawn** **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:onPlayerRespawned', function(_clientCoords, spawnType) if GetInvokingResource() then return end local src = source if spawnType == 'hospital' then Bridge.RemoveMoney(src, 'bank', 500) end end) ``` **NPC Hospital Check-In** Place this at the end of an existing server check-in handler after the NPC treatment progress succeeds. Validate the treatment authorization, NPC proximity, payment, and any EMS availability rule on the server before calling the export. Do not expose the call through an unrestricted client event. ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, { ClearDiseases = false }) if not ok then return false, reason end return true ``` To select a configured spawn group and keep diseases for this respawn, pass the hospital key as the third argument: ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, { ClearDiseases = false }, 'prison') ``` To select a spawn group with the global reset settings, pass `nil` for the settings: ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, nil, 'prison') ``` The hospital key can also be passed as the second argument when using the global reset settings: ```lua local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, 'prison') ``` **Log Deaths to Database** **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:onPlayerDeath', function() if GetInvokingResource() then return end local src = source MySQL.insert('INSERT INTO death_logs (identifier, cause, killer) VALUES (?, ?, ?)', { Bridge.GetLicense(src), PlayerDeathContext[src] and PlayerDeathContext[src].deathReason or 'unknown', PlayerDeathContext[src] and PlayerDeathContext[src].attackerServerId or 'environment' }) end) ``` **Check Disease from External Script** On the server, pass the target player's server ID: ```lua local infected = exports.ml_healthsystem:HasDisease(source, 'influenza') ``` On the client, check the local player: ```lua local infected = exports.ml_healthsystem:HasDisease('influenza') ``` **Custom Life-Point Penalty on Player Kill** **open/server/handlers.lua** ```lua RegisterNetEvent('ml_healthsystem:server:onPlayerDeath', function() if GetInvokingResource() then return end local src = source local ctx = PlayerDeathContext[src] if ctx and ctx.killedByPlayer then exports.ml_healthsystem:RemoveLifePoints(src, 15) end end) ``` The respawn flow already applies a life-point penalty internally via `CalculatePenalty` (highest-matching zone / external reason / NPC+weapon / player-kill / weapon penalty wins, no stacking). Use the export above only for extra, custom deductions. Add the calling resource to Config.LifePoints.ExportAllowlist first. **Transform, change profile and cure from another resource** Use these calls after your server script has validated the action and target. Add the calling resource to `ExportAllowlist` if you use a restricted list. ```lua local ok, state = exports.ml_healthsystem:TransformPlayer(playerId, { profile = 'default', reason = 'story_event', duration = 600, }) if not ok then return false, state end local changed, result = exports.ml_healthsystem:SetZombieProfile(playerId, 'default', { ignoreByZombies = false, }) if not changed then return false, result end return exports.ml_healthsystem:CureZombie(playerId, { reason = 'special_treatment', }) ``` **Connect an NPC zombie resource** Your NPC resource owns zombie spawning, movement, attacks and target selection. Call `ApplyZombieBite` on the server after a validated NPC bite succeeds. Use `ShouldZombiesIgnorePlayer` when choosing a target and again immediately before an attack. On a state change, discard a protected target and choose another. Reconcile existing targets when your resource starts. ```lua local function canTargetPlayer(playerId) return not exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId) end ``` For client-driven NPC AI, queue a refresh for visible candidates and react to the client event. This predicate distinguishes unavailable state from a target that is confirmed to be unprotected: ```lua local function canTargetPlayer(playerId) local ignored, reason = exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId) if ignored == nil then return nil, reason end return not ignored end exports.ml_healthsystem:RefreshZombieStates({ targetServerId }) AddEventHandler('ml_healthsystem:client:ZombieChanged', function(playerId, state, previous) MyClientZombieAI.RefreshPlayerTarget(playerId) end) ``` `targetServerId` and `MyClientZombieAI` belong to your NPC integration. Replace them with its actual candidate and target refresh function. Choose how your AI handles a disabled feature or unavailable state. Use the server export for authoritative decisions; the `medical:zombie` state bag must not grant immunity by itself.