Developer
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.
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
1exports.ml_healthsystem:AddDisease(source, diseaseName)AddDisease(source, diseaseName): Infects a player with a disease defined inConfig.Diseases. Returnstrueon success,falseif the disease key does not exist or is already active.
1exports.ml_healthsystem:CureDiseaseForce(source, diseaseName)CureDiseaseForce(source, diseaseName): Force-cures a specific disease, bypassingmaxCurableStagechecks. Returnstrueif cured.
1exports.ml_healthsystem:CureAllDiseases(source)CureAllDiseases(source): Removes all active diseases from a player.
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 ofConfig.Diseasesas an array of{ id, label, icon, severity }, sorted by label.
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
1local ok, reason = exports.ml_healthsystem:RevivePlayer(source, { ClearDiseases = false })RevivePlayer(source, settings?): Revives a player in place, usingConfig.ReviveSettingsinserver/config_server.luawith 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:
1local ok, reason = exports.ml_healthsystem:RevivePlayer(source)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 indeadorlast_stand.
Reset Options Reference
settings: Optional table for either export. Include only the boolean fields to change. Omitted fields inheritConfig.ReviveSettingsforRevivePlayerorConfig.HospitalResetforHospitalRespawn. The global configuration and the supplied table are not modified.RestoreHealth: Restore full health whentrue. Whenfalse, keep the pre-reset health read by the server, clamped between 101 andConfig.MaxHealthso the player can return alive.RestoreBlood: Restore blood volume whentrue; preserve the current volume whenfalse.ResetBleeding: Stop bleeding whentrue; preserve the current bleeding state whenfalse.ClearInjuries: Remove injuries whentrue; preserve the existing wounds whenfalse.ClearFractures: Remove fractures whentrue; preserve them whenfalse.ClearCasts: Remove casts whentrue; preserve them whenfalse.ClearDiseases: Remove diseases whentrue; preserve them whenfalse.ResetHungerThirst: Reset hunger, thirst, and stress whentrue; preserve them whenfalse.
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 fromConfig.StartSpawninshared/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 isprison. Omit the key to use a random hospital whenConfig.RespawnSettings.RandomSpawnis true, otherwise the nearest hospital. With no hospital configured, the flow usesConfig.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 neitherdeadnorlast_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.HospitalRespawnalso 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.
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.
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.
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 feedsConfig.LifePoints.ExternalReasonspenalty matching. Calls from outside the resource are gated to an allowlist (ml_radiation,ml_climaticvitals); foreign resources are rejected.
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. Returnstruewhen the player is dead or in last stand
Field Treatment
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.
1exports.ml_healthsystem:AddBloodRegenBuff(source)AddBloodRegenBuff(source, durationSeconds): Applies a blood regeneration boost.
Blood types and transfusions
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.reasoniscompatible,incompatible,unknown_bagorinvalid_patient. A missing player state returnsfalse, '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. ThebloodTypefield returned byGetMedicalData(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.
1exports.ml_healthsystem:GetLifePoints(source)GetLifePoints(source): Returns the player's current life points (reads/creates theml_lifepointsrow). Returns-1if the system is disabled.
1exports.ml_healthsystem:GetLifePointsMax(source)GetLifePointsMax(): Returns the maximum (Config.LifePoints.StartingPoints). Returns-1if disabled.
1exports.ml_healthsystem:IsLifePointsEnabled()IsLifePointsEnabled(): Returnstrueif the life points system is active.
1exports.ml_healthsystem:SetLifePoints(source, value)SetLifePoints(source, value): Sets a player's life points to an exact value, clamped to[MinPoints, StartingPoints]. Returnstrueon success.
1exports.ml_healthsystem:AddLifePoints(source, amount)AddLifePoints(source, amount): Adds life points (clamped to max). Returnstrueon success.
1exports.ml_healthsystem:RemoveLifePoints(source, amount)RemoveLifePoints(source, amount): Removes life points (clamped to min). Returnstrueon success.
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.
1local inRecovery, secondsLeft = exports.ml_healthsystem:IsInRecovery(source)
2exports.ml_healthsystem:EndRecovery(source, true)IsInRecovery(source): Returnstrue, secondsLeftwhile the player's character is in recovery, otherwisefalse, 0.EndRecovery(source, restorePoints?): Ends the recovery now. Life points are set toRestorePointsunlessrestorePointsisfalse. Gated by the same allowlist as the mutators. Returnstrueon success.
1AddEventHandler('ml_healthsystem:server:recoveryStarted', function(source, identifier, duration)
2end)
3
4AddEventHandler('ml_healthsystem:server:recoveryEnded', function(source, identifier)
5end)recoveryStarted: a character reachedMinPointsand entered recovery.durationis in seconds.recoveryEnded: the recovery finished, by timer, byEndRecoveryor because life points were raised.
While a character is in recovery, the replicated state bag medical:recovery is true.
Spawn Points and Tents
1exports.ml_healthsystem:GetSpawnPoints()GetSpawnPoints(): Returns the configured spawn table (Config.StartSpawnfromshared/config/respawn.lua).
1exports.ml_healthsystem:GetTentCoordsById(tentId)GetTentCoordsById(tentId): Returnsx, y, z, heading, ownerfor a placed tent fromml_health_tents.
Buffs
1exports.ml_healthsystem:GetActiveBuffs(source)GetActiveBuffs(source): Returns the timed effects running on a player:bloodRegen,tempBandage,painReliefandmedication. Each present entry hasactiveandsecondsLeft;painReliefaddsitemandmedicationaddsdoses. 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.
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.radiationis set only whileml_radiationis started.lifePointsis -1 when Life Points are disabled.isDeadis true for bothdeadandlast_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 toConfig.Blood.MaxVolume.GetBleedLevel(source): Returns the bleeding level, 0 when the player is not bleeding.GetFractures(source)andGetCasts(source): Return the fracture and cast tables keyed by body part, or an empty table.GetDeathState(source): Returnsalive,last_standordead.
Pain Relief and Medication
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 forsecondsand replicates it to the client.itemis an optional label stored with the effect. A new call replaces the running effect. Returns false whensecondsis 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.expiresAtis anos.time()timestamp.GetMedicationDoses(source): Returns the number of doses still inside their cooldown, keyed bydoseGroupor by item name. Only items withmaxDosesinshared/config/items.luaare 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
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. Everyoptsfield is optional:part(body zone, random when omitted),damage(defaults toConfig.ZombieBite.Damage),infect(set it tofalseto skip the infection roll),chance(infection chance in percent for this bite) andforce(infect without rolling). Returnsfalsewhen the player is offline, dead or when the feature is disabled. Health damage stays with the calling script.
1exports.ml_healthsystem:InfectFromContact(source, opts)InfectFromContact(source, opts): rolls the contact infection on its own, with no wound.optsacceptsdisease(defaults toConfig.ContactInfection.Disease),chanceandforce. The roll always runs server-side, and it is skipped when the player already carries the disease or is not alive.
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.
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.
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
1exports.ml_healthsystem:HasDisease(diseaseName)HasDisease(diseaseName): Returnstrueif the local player has the disease (reads themedical:diseaseslive state).
1exports.ml_healthsystem:GetActiveDiseases()GetActiveDiseases(): Returns the table of active diseases on the local player.
1exports.ml_healthsystem:AddDisease(diseaseName)AddDisease(diseaseName): Requests the server to infect the local player.
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
1exports.ml_healthsystem:isDead()isDead(): Returnstrueif the local player is dead or in last stand.
1exports.ml_healthsystem:Revive()Revive(): Requests a self-revive from the server (staff-gated server-side).
1exports.ml_healthsystem:SetDeathDisabled(disabled)SetDeathDisabled(disabled): Sets the localdisableDeathSystemlive state.
System State
1exports.ml_healthsystem:isSystemReady()isSystemReady(): Returnstrueonce the medical system has initialized.
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.
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, withoutradiationand withready, true once the local medical system has loaded.diseaseLevelis set only for the local player.health,maxHealthandarmourare nil when the other player's ped does not exist on this client.GetBloodPercent(serverId?): Returns the blood volume as a rounded percentage ofConfig.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
1exports.ml_healthsystem:GetLastDamageData()GetLastDamageData(): Returns the last damage context:weaponHash,weaponClass,attackerId,bodyPart,attackerModel,timestamp.weaponClassdefaults to'UNKNOWN'andbodyPartto'chest'when no context exists.
1exports.ml_healthsystem:BuildDeathData()BuildDeathData(): Builds a death object from the last damage event:weaponHash,weaponClass,attackerId,isPlayerKill,bodyPart,cause,timestamp.causeis derived fromweaponClass(gunshot, explosion, stabbing, fall, burn, etc.).
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.partis a body zone like'left_leg', or'random'to pick one.amountdefaults to15,weaponClassoverrides the wound type. Returnstruewhen a wound landed. Call it on the victim's client from an external damage source such as a zombie script.
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 exceptforce. The infection roll still happens on the server.
Life Points (Client)
1exports.ml_healthsystem:GetLifePoints()GetLifePoints(): Returns the local player's life points from thelifepointslive state. Returns-1if disabled.
1exports.ml_healthsystem:GetLifePointsMax()GetLifePointsMax(): ReturnsConfig.LifePoints.StartingPoints. Returns-1if disabled.
1exports.ml_healthsystem:IsLifePointsEnabled()IsLifePointsEnabled(): Returnstrueif the life points system is active.IsInRecovery(): Returnstruewhile the local character is held in recovery.
Tent (Client)
1exports.ml_healthsystem:GetPlayerTentClient()GetPlayerTentClient(): Returns the local player's tracked tent data on the client.
Health Panel
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 eventml_healthsystem:client:OpenHealthPanel, which the server can fire withTriggerClientEvent.
1Config.HealthMenu = {
2 Command = 'health',
3 Keybind = false,
4}Command: chat command that opens the panel. Set it tofalseto 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.falseleaves it unbound.
Zombie state and integrations
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.
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.
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).
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.deathDataholds the weapon, weapon class, body part, attacker and causeonPlayerLastStand: the local player goes down into last standonPlayerRespawned: the local player respawns.spawnTypeishospital,bed,tent,randomorlast_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.
1AddEventHandler('ml_healthsystem:server:ZombieStateChanged', function(playerId, state, previous)
2end)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.
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
17endOnPresentationChanged 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.DisableHealthRegenvitals.lua: blood, bandages, armor, wound levels, blood trails, cast durationblood_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) andConfig.XpCooldownSecondsanim_presets.lua: animation presets reused by itemsmasks.lua: protective masks (customer-editable)fractures.lua: fracture model wiringweapons.lua: weapon-class wound profiles, weapon→class map, body-part health thresholdsrespawn.lua:Config.RespawnSettings,Config.StartSpawn,Config.HrsBase,Config.RespawnTentlifecycle.lua: last stand and death tuningeffects.lua: visual effects, voice,Config.Flare, vehicle crash, NPC interactionui.lua: scan UI, medical jobs, commandsintegrations.lua: radiation integration, contact infection, andConfig.LifePoints.Zonesitem_diseases.lua:Config.ItemDiseases(food/item → disease wiring)security.lua:Config.AntiBunnyHopopen.lua:Config.OpenHandlersevent 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:
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.
1local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, { ClearDiseases = false })
2if not ok then
3 return false, reason
4end
5return trueTo select a configured spawn group and keep diseases for this respawn, pass the hospital key as the third argument:
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:
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:
1local ok, reason = exports.ml_healthsystem:HospitalRespawn(source, 'prison')On the server, pass the target player's server ID:
1local infected = exports.ml_healthsystem:HasDisease(source, 'influenza')On the client, check the local player:
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.
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.
1local function canTargetPlayer(playerId)
2 return not exports.ml_healthsystem:ShouldZombiesIgnorePlayer(playerId)
3endFor 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:
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.