Developer

25 min readUpdated Yesterday

Overview

Other resources work with ML HealthSystem through server and client exports and local lifecycle events. The editable file is open/client/zombie_handlers.lua, for the zombie presentation.

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.

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
1exports.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
1exports.ml_healthsystem:CureDiseaseForce(source, diseaseName)
  • CureDiseaseForce(source, diseaseName): Force-cures a specific disease, bypassing maxCurableStage checks. Returns true if cured.
lua
1exports.ml_healthsystem:CureAllDiseases(source)
  • CureAllDiseases(source): Removes all active diseases from a player.
lua
1exports.ml_healthsystem:HasDisease(source, diseaseName)
2exports.ml_healthsystem:GetDiseaseList()
  • HasDisease(source, diseaseName): Returns true when the disease is active on the player.
  • GetDiseaseList(): Returns every entry of Config.Diseases as an array of { id, label, icon, severity }, sorted by label.
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
1local 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
1local ok, reason = exports.ml_healthsystem:RevivePlayer(source)
lua
1local 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
1local 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
1exports.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
1exports.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.
lua
1local ok = exports.ml_healthsystem:AuthorizeExternalRespawn(source)
  • AuthorizeExternalRespawn(source): lets your own respawn resource reset a dead or downed player within the next 10 seconds. Returns true when the player is dead or in last stand

Field Treatment

lua
1exports.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
1exports.ml_healthsystem:AddBloodRegenBuff(source)
  • AddBloodRegenBuff(source, durationSeconds): Applies a blood regeneration boost.

Blood types and transfusions

lua
1local compatible, reason, normalizedDonor = exports.ml_healthsystem:CanReceiveBlood(playerId, donorType)
2local settings = exports.ml_healthsystem:GetBloodTransfusionSettings()
3local bloodType = exports.ml_healthsystem:GetBloodType(playerId)
  • CanReceiveBlood(playerId, donorType): checks a donor type against the player's current server medical state and configured compatibility rules. Returns a boolean, a reason and the normalized donor type, which may be nil.
  • reason is compatible, incompatible, unknown_bag or invalid_patient. A missing player state returns false, 'invalid_patient'. The check does not consume an item, restore blood or apply damage.
  • GetBloodTransfusionSettings(): returns a new table containing the validated transfusion options. Changing the returned table does not change the resource configuration.
  • GetBloodType(playerId): returns the current stored type or nil before it is available. The bloodType field returned by GetMedicalData(playerId) can also be nil; integrations must wait for medical state instead of assuming A or A+.

External inventory code must preserve the exact bag slot and validate its current metadata when consuming it. The export checks a type, so the calling resource still owns its item consumption and treatment flow.

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
1exports.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
1exports.ml_healthsystem:GetLifePointsMax(source)
  • GetLifePointsMax(): Returns the maximum (Config.LifePoints.StartingPoints). Returns -1 if disabled.
lua
1exports.ml_healthsystem:IsLifePointsEnabled()
  • IsLifePointsEnabled(): Returns true if the life points system is active.
lua
1exports.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
1exports.ml_healthsystem:AddLifePoints(source, amount)
  • AddLifePoints(source, amount): Adds life points (clamped to max). Returns true on success.
lua
1exports.ml_healthsystem:RemoveLifePoints(source, amount)
  • RemoveLifePoints(source, amount): Removes life points (clamped to min). Returns true on success.
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.

lua
1local inRecovery, secondsLeft = exports.ml_healthsystem:IsInRecovery(source)
2exports.ml_healthsystem:EndRecovery(source, true)
  • IsInRecovery(source): Returns true, secondsLeft while the player's character is in recovery, otherwise false, 0.
  • EndRecovery(source, restorePoints?): Ends the recovery now. Life points are set to RestorePoints unless restorePoints is false. Gated by the same allowlist as the mutators. Returns true on success.
your_script/server.lua
1AddEventHandler('ml_healthsystem:server:recoveryStarted', function(source, identifier, duration)
2end)
3
4AddEventHandler('ml_healthsystem:server:recoveryEnded', function(source, identifier)
5end)
  • recoveryStarted: a character reached MinPoints and entered recovery. duration is in seconds.
  • recoveryEnded: the recovery finished, by timer, by EndRecovery or because life points were raised.

While a character is in recovery, the replicated state bag medical:recovery is true.

Spawn Points and Tents

lua
1exports.ml_healthsystem:GetSpawnPoints()
  • GetSpawnPoints(): Returns the configured spawn table (Config.StartSpawn from shared/config/respawn.lua).
lua
1exports.ml_healthsystem:GetTentCoordsById(tentId)
  • GetTentCoordsById(tentId): Returns x, y, z, heading, owner for a placed tent from ml_health_tents.

Buffs

lua
1exports.ml_healthsystem:GetActiveBuffs(source)
  • GetActiveBuffs(source): Returns the timed effects running on a player: bloodRegen, tempBandage, painRelief and medication. Each present entry has active and secondsLeft; painRelief adds item and medication adds doses. Inactive effects are nil.

Medical Data

Read-only getters for other resources. source is an online player's server ID. Values come from the server's medical state.

lua
1local data      = exports.ml_healthsystem:GetMedicalData(source)
2local injuries  = exports.ml_healthsystem:GetInjuries(source)
3local level     = exports.ml_healthsystem:GetWoundLevel(source)
4local blood     = exports.ml_healthsystem:GetBloodLevel(source)
5local bleeding  = exports.ml_healthsystem:GetBleedLevel(source)
6local fractures = exports.ml_healthsystem:GetFractures(source)
7local casts     = exports.ml_healthsystem:GetCasts(source)
8local state     = exports.ml_healthsystem:GetDeathState(source)
  • GetMedicalData(source): Returns one snapshot table, or nil when the player is offline. Fields: serverId, health, maxHealth, armour, bloodLevel, bloodMax, bloodPercent, bloodType, bleeding, woundLevel, injuries, fractures, casts, diseases, zombie, painRelief, radiation, deathState, isDead, deathTime, lifePoints.
  • radiation is set only while ml_radiation is started. lifePoints is -1 when Life Points are disabled. isDead is true for both dead and last_stand.
  • GetInjuries(source): Returns only the injured body parts, keyed by part: head, neck, chest, spine, pelvis, left_arm, right_arm, left_leg, right_leg. Each value maps a wound type to its severity.
  • GetWoundLevel(source): Returns the worst wound severity across all body parts, from 0 to 4.
  • GetBloodLevel(source): Returns the blood volume, from 0 to Config.Blood.MaxVolume.
  • GetBleedLevel(source): Returns the bleeding level, 0 when the player is not bleeding.
  • GetFractures(source) and GetCasts(source): Return the fracture and cast tables keyed by body part, or an empty table.
  • GetDeathState(source): Returns alive, last_stand or dead.

Pain Relief and Medication

lua
1exports.ml_healthsystem:ApplyPainRelief(source, seconds, item)
2exports.ml_healthsystem:ClearPainRelief(source)
3exports.ml_healthsystem:IsPainRelieved(source)
4exports.ml_healthsystem:GetPainRelief(source)
5exports.ml_healthsystem:GetMedicationDoses(source)
  • ApplyPainRelief(source, seconds, item?): Starts pain relief for seconds and replicates it to the client. item is an optional label stored with the effect. A new call replaces the running effect. Returns false when seconds is missing or not positive.
  • ClearPainRelief(source): Ends pain relief immediately and notifies the client.
  • IsPainRelieved(source): Returns true while pain relief has time left.
  • GetPainRelief(source): Returns { expiresAt, duration, item }, or nil when no relief is active. expiresAt is an os.time() timestamp.
  • GetMedicationDoses(source): Returns the number of doses still inside their cooldown, keyed by doseGroup or by item name. Only items with maxDoses in shared/config/items.lua are counted.

ApplyPainRelief and ClearPainRelief accept calls from any server resource. Check the player's permissions in your own server event before calling them.

Zombie Bites

lua
1exports.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
1exports.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
1Config.ZombieBite = {
2    Enabled     = true,
3    WeaponClass = 'ZOMBIE_BITE',
4    Damage      = 12,
5    Infect      = true,
6    Chance      = nil,
7    Parts       = { 'left_arm', 'right_arm', 'neck', 'chest', 'left_leg', 'right_leg' },
8}

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
1exports.ml_healthsystem:TransformPlayer(playerId, options)
2exports.ml_healthsystem:CureZombie(playerId, options)
3exports.ml_healthsystem:SetZombieProfile(playerId, profileId, options)
4exports.ml_healthsystem:GetZombieState(playerId)
5exports.ml_healthsystem:IsZombie(playerId)
6exports.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.

lua
1{
2    version = 1,
3    playerId = 42,
4    transformed = true,
5    active = true,
6    profile = 'default',
7    reason = 'infection',
8    revision = 3,
9    session = 1791198001,
10    transformedAt = 1791198000,
11    expiresAt = 0,
12    ignoreByZombies = true,
13}

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
1exports.ml_healthsystem:HasDisease(diseaseName)
  • HasDisease(diseaseName): Returns true if the local player has the disease (reads the medical:diseases live state).
lua
1exports.ml_healthsystem:GetActiveDiseases()
  • GetActiveDiseases(): Returns the table of active diseases on the local player.
lua
1exports.ml_healthsystem:AddDisease(diseaseName)
  • AddDisease(diseaseName): Requests the server to infect the local player.
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
1exports.ml_healthsystem:isDead()
  • isDead(): Returns true if the local player is dead or in last stand.
lua
1exports.ml_healthsystem:Revive()
  • Revive(): Requests a self-revive from the server (staff-gated server-side).
lua
1exports.ml_healthsystem:SetDeathDisabled(disabled)
  • SetDeathDisabled(disabled): Sets the local disableDeathSystem live state.

System State

lua
1exports.ml_healthsystem:isSystemReady()
  • isSystemReady(): Returns true once the medical system has initialized.
lua
1local bloodType = exports.ml_healthsystem:GetBloodType()
2local medicalData = exports.ml_healthsystem:GetMedicalData()

GetBloodType() and GetMedicalData().bloodType return the local medical type when available. The type can be nil while state is loading. Use the server compatibility export for a treatment decision.

Medical Data (Client)

Client versions of the same getters read the replicated state. serverId? is optional: omit it for the local player or pass another player's server ID.

lua
1local data = exports.ml_healthsystem:GetMedicalData()
2exports.ml_healthsystem:GetInjuries(serverId)
3exports.ml_healthsystem:GetWoundLevel(serverId)
4exports.ml_healthsystem:GetBloodLevel(serverId)
5exports.ml_healthsystem:GetBloodPercent(serverId)
6exports.ml_healthsystem:GetBleedLevel(serverId)
7exports.ml_healthsystem:GetFractures(serverId)
8exports.ml_healthsystem:GetCasts(serverId)
9exports.ml_healthsystem:GetDeathState(serverId)
10exports.ml_healthsystem:GetPainRelief(serverId)
11exports.ml_healthsystem:IsPainRelieved(serverId)
  • GetMedicalData(serverId?): Same snapshot as the server export, without radiation and with ready, true once the local medical system has loaded. diseaseLevel is set only for the local player. health, maxHealth and armour are nil when the other player's ped does not exist on this client.
  • GetBloodPercent(serverId?): Returns the blood volume as a rounded percentage of Config.Blood.MaxVolume.
  • IsPainRelieved(serverId?): Returns true while the pain relief state is set. It does not check the expiry time, so use the server export for gameplay decisions.
  • The other getters return the same values as their server versions. Client values are for display and do not authorize gameplay changes.

Damage and Combat

lua
1exports.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
1exports.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
1exports.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
1exports.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
1exports.ml_healthsystem:GetLifePoints()
  • GetLifePoints(): Returns the local player's life points from the lifepoints live state. Returns -1 if disabled.
lua
1exports.ml_healthsystem:GetLifePointsMax()
  • GetLifePointsMax(): Returns Config.LifePoints.StartingPoints. Returns -1 if disabled.
lua
1exports.ml_healthsystem:IsLifePointsEnabled()
  • IsLifePointsEnabled(): Returns true if the life points system is active.
  • IsInRecovery(): Returns true while the local character is held in recovery.

Tent (Client)

lua
1exports.ml_healthsystem:GetPlayerTentClient()
  • GetPlayerTentClient(): Returns the local player's tracked tent data on the client.

Health Panel

lua
1exports.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
1Config.HealthMenu = {
2    Command = 'health',
3    Keybind = false,
4}
  • 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
1exports.ml_healthsystem:GetZombieState(playerId)
2exports.ml_healthsystem:IsZombie(playerId)
3exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId)
4exports.ml_healthsystem:RefreshZombieStates(playerIds)
5exports.ml_healthsystem:GetZombieCapabilities()
6exports.ml_healthsystem:RegisterZombieInventoryProvider(name, provider)
7exports.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. 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.

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
1local unregister = exports.ml_healthsystem:RegisterZombieInventoryProvider('my_inventory', {
2    SetLocked = function(locked, state)
3        return MyInventory.SetMedicalZombieLock(locked)
4    end,
5    Close = function()
6        return MyInventory.Close()
7    end,
8    SetWeaponsBlocked = function(blocked, state)
9        return MyInventory.SetMedicalZombieWeaponLock(blocked)
10    end,
11})

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.

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
1local unregister = exports.ml_healthsystem:RegisterZombieAppearanceProvider('my_appearance', {
2    Capture = function(ped)
3        return MyAppearance.CaptureCompleteCharacter(ped)
4    end,
5    Restore = function(ped, original)
6        return MyAppearance.RestoreCompleteCharacter(ped, original)
7    end,
8    Apply = function(ped, profile, state)
9        return MyAppearance.ApplyZombiePresentation(ped, profile, state)
10    end,
11})

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 player who transforms changes model, and other players see the same change. 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.

Lifecycle Events

Your own client scripts can listen to these local events. Each one fires only when its toggle is on in Config.OpenHandlers (shared/config/open.lua).

your_script/client.lua
1AddEventHandler('ml_healthsystem:onPlayerDeath', function(deathData)
2end)
3
4AddEventHandler('ml_healthsystem:onPlayerLastStand', function(damageData)
5end)
6
7AddEventHandler('ml_healthsystem:onPlayerRespawned', function(spawnCoords, spawnType)
8end)
  • onPlayerDeath: the local player dies. deathData holds the weapon, weapon class, body part, attacker and cause
  • onPlayerLastStand: the local player goes down into last stand
  • onPlayerRespawned: the local player respawns. spawnType is hospital, bed, tent, random or last_position

The death, respawn and revive handling inside the script is not editable. Item-triggered diseases are set in shared/config/item_diseases.lua (Config.ItemDiseases) and need no code.

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
1AddEventHandler('ml_healthsystem:server:ZombieStateChanged', function(playerId, state, previous)
2end)
lua
1AddEventHandler('ml_healthsystem:client:ZombieChanged', function(playerId, state, previous)
2end)

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
1function ZombieHandlers.OnPresentationChanged(playerId, ped, state, previous)
2end
3
4function ZombieHandlers.OnPresentationCleared(playerId, ped, state)
5end
6
7function ZombieHandlers.RestoreMovement(ped)
8    return false
9end
10
11function ZombieHandlers.GetInventoryProvider()
12    return nil
13end
14
15function ZombieHandlers.GetAppearanceProvider(profile)
16    return nil
17end

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.

  • core.lua: Config.Debug, Config.Locale, Config.Theme, Config.DisableHealthRegen
  • vitals.lua: blood, bandages, armor, wound levels, blood trails, cast duration
  • blood_groups.lua: blood-type assignment weights, compatibility modes, reaction policy, legacy Rh handling and disease transmission.
  • 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, masks and blood-group rules are customer-editable in their config files. The escrowed internal/balance/*.lua files contain performance throttles, damage control, weapon penalties, AntiBunnyHop sensitivity presets and body-part balance. Editable AntiBunnyHop settings are in security.lua:

shared/config/security.lua
1Config.AntiBunnyHop = {
2    Enabled     = true,
3    Sensitivity = 'medium',
4    Notify      = true,
5    Message     = "Slow down! Don't jump so often.",
6}
  • 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

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
1local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, { ClearDiseases = false })
2if not ok then
3    return false, reason
4end
5return true

To select a configured spawn group and keep diseases for this respawn, pass the hospital key as the third argument:

lua
1local 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
1local 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
1local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, 'prison')

On the server, pass the target player's server ID:

lua
1local infected = exports.ml_healthsystem:HasDisease(source, 'influenza')

On the client, check the local player:

lua
1local infected = exports.ml_healthsystem:HasDisease('influenza')

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.

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
1local ok, state = exports.ml_healthsystem:TransformPlayer(playerId, {
2    profile = 'default',
3    reason = 'story_event',
4    duration = 600,
5})
6if not ok then
7    return false, state
8end
9
10local changed, result = exports.ml_healthsystem:SetZombieProfile(playerId, 'default', {
11    ignoreByZombies = false,
12})
13if not changed then
14    return false, result
15end
16
17return exports.ml_healthsystem:CureZombie(playerId, {
18    reason = 'special_treatment',
19})

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
1local function canTargetPlayer(playerId)
2    return not exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId)
3end

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
1local function canTargetPlayer(playerId)
2    local ignored, reason = exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId)
3    if ignored == nil then
4        return nil, reason
5    end
6    return not ignored
7end
8
9exports.ml_healthsystem:RefreshZombieStates({ targetServerId })
10
11AddEventHandler('ml_healthsystem:client:ZombieChanged', function(playerId, state, previous)
12    MyClientZombieAI.RefreshPlayerTarget(playerId)
13end)

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.