2025-04-07 01:41:12 +00:00

232 lines
6.4 KiB
Lua

local config = require 'config.client'
local sharedConfig = require 'config.shared'
---@type table<number, boolean> weapon hashes as a set
WeaponsThatDamagedPlayer = {}
NumInjuries = 0
local playerState = LocalPlayer.state
---@type table<BodyPartKey, Injury>
Injuries = {}
for bodyPartKey in pairs(sharedConfig.bodyParts) do
local bodyPartStateBag = BODY_PART_STATE_BAG_PREFIX .. bodyPartKey
Injuries[bodyPartKey] = playerState[bodyPartStateBag]
AddStateBagChangeHandler(bodyPartStateBag, ('player:%s'):format(cache.serverId), function(_, _, value)
Injuries[bodyPartKey] = value
end)
end
function SetInjury(bodyPartKey, severity)
playerState:set(BODY_PART_STATE_BAG_PREFIX .. bodyPartKey, severity, true)
end
BleedLevel = playerState[BLEED_LEVEL_STATE_BAG] or 0
AddStateBagChangeHandler(BLEED_LEVEL_STATE_BAG, ('player:%s'):format(cache.serverId), function(_, _, value)
BleedLevel = value
end)
function SetBleedLevel(level)
playerState:set(BLEED_LEVEL_STATE_BAG, level, true)
end
DeathState = playerState[DEATH_STATE_STATE_BAG] or sharedConfig.deathState.ALIVE
AddStateBagChangeHandler(DEATH_STATE_STATE_BAG, ('player:%s'):format(cache.serverId), function(_, _, value)
DeathState = value
end)
function SetDeathState(deathState)
playerState:set(DEATH_STATE_STATE_BAG, deathState, true)
DeathState = deathState
end
BleedTickTimer, AdvanceBleedTimer = 0, 0
FadeOutTimer, BlackoutTimer = 0, 0
---@type number
Hp = nil
DeathTime = 0
LaststandTime = 0
RespawnHoldTime = 5
LastStandDict = 'combat@damage@writhe'
LastStandAnim = 'writhe_loop'
exports('IsDead', function()
return DeathState == sharedConfig.deathState.DEAD
end)
exports('IsLaststand', function()
return DeathState == sharedConfig.deathState.LAST_STAND
end)
exports('GetDeathTime', function()
return DeathTime
end)
exports('GetLaststandTime', function()
return LaststandTime
end)
exports('IncrementDeathTime', function(seconds)
DeathTime += seconds
end)
exports('IncrementLaststandTime', function(seconds)
LaststandTime += seconds
end)
exports('GetRespawnHoldTimeDeprecated', function()
return RespawnHoldTime
end)
lib.callback.register('qbx_medical:client:killPlayer', function()
SetEntityHealth(cache.ped, 0)
end)
---@return boolean isInjuryCausingLimp if injury causes a limp and is damaged.
local function isInjuryCausingLimp()
for bodyPartKey in pairs(Injuries) do
if sharedConfig.bodyParts[bodyPartKey].causeLimp then
return true
end
end
return false
end
---notify the player of damage to their body.
local function doLimbAlert()
if DeathState ~= sharedConfig.deathState.ALIVE or NumInjuries == 0 then return end
local limbDamageMsg = ''
if NumInjuries <= config.alertShowInfo then
local i = 0
for bodyPartKey, injury in pairs(Injuries) do
local bodyPart = sharedConfig.bodyParts[bodyPartKey]
limbDamageMsg = limbDamageMsg .. locale('info.pain_message', bodyPart.label, sharedConfig.woundLevels[injury.severity].label)
i += 1
if i < NumInjuries then
limbDamageMsg = limbDamageMsg .. ' | '
end
end
else
limbDamageMsg = locale('info.many_places')
end
exports.qbx_core:Notify(limbDamageMsg, 'error')
end
---sets ped animation to limping and prevents running.
function MakePedLimp()
if not isInjuryCausingLimp() then return end
lib.requestAnimSet('move_m@injured', 5000)
SetPedMovementClipset(cache.ped, 'move_m@injured', 1)
SetPlayerSprint(cache.playerId, false)
end
--- TODO: this export should not check any conditions, but force the ped to limp instead.
exports('MakePedLimp', MakePedLimp)
local function resetMinorInjuries()
for bodyPartKey, injury in pairs(Injuries) do
if injury.severity <= 2 then
SetInjury(bodyPartKey, nil)
NumInjuries -= 1
end
end
if BleedLevel <= 2 then
SetBleedLevel(0)
BleedTickTimer = 0
AdvanceBleedTimer = 0
FadeOutTimer = 0
BlackoutTimer = 0
end
SendBleedAlert()
MakePedLimp()
doLimbAlert()
end
local function resetAllInjuries()
for bodyPartKey in pairs(sharedConfig.bodyParts) do
SetInjury(bodyPartKey, nil)
end
NumInjuries = 0
SetBleedLevel(0)
BleedTickTimer = 0
AdvanceBleedTimer = 0
FadeOutTimer = 0
BlackoutTimer = 0
WeaponsThatDamagedPlayer = {}
SendBleedAlert()
MakePedLimp()
doLimbAlert()
lib.callback.await('qbx_medical:server:resetHungerAndThirst')
end
---notify the player of bleeding to their body.
function SendBleedAlert()
if DeathState == sharedConfig.deathState.DEAD or BleedLevel == 0 then return end
exports.qbx_core:Notify(locale('info.bleed_alert', sharedConfig.bleedingStates[BleedLevel]), 'inform')
end
exports('SendBleedAlert', SendBleedAlert)
---adds a bleed to the player and alerts them. Total bleed level maxes at 4.
---@param level 1|2|3|4 speed of the bleed
function ApplyBleed(level)
if BleedLevel == 4 then return end
local newBleedLevel = level + BleedLevel
if newBleedLevel > 4 then
SetBleedLevel(4)
else
SetBleedLevel(newBleedLevel)
end
SendBleedAlert()
end
---heals player wounds.
---@param type? 'full'|any heals all wounds if full otherwise heals only major wounds.
RegisterNetEvent('qbx_medical:client:heal', function(type)
if type == 'full' then
resetAllInjuries()
else
resetMinorInjuries()
end
exports.qbx_core:Notify(locale('success.wounds_healed'), 'success')
end)
CreateThread(function()
while true do
Wait((1000 * config.messageTimer))
doLimbAlert()
end
end)
---Revives player, healing all injuries
RegisterNetEvent('qbx_medical:client:playerRevived', function()
if DeathState ~= sharedConfig.deathState.ALIVE then
local pos = GetEntityCoords(cache.ped, true)
NetworkResurrectLocalPlayer(pos.x, pos.y, pos.z, GetEntityHeading(cache.ped), true, false)
SetDeathState(sharedConfig.deathState.ALIVE)
SetEntityInvincible(cache.ped, false)
EndLastStand()
end
SetEntityMaxHealth(cache.ped, 200)
SetEntityHealth(cache.ped, 200)
ClearPedBloodDamage(cache.ped)
SetPlayerSprint(cache.playerId, true)
resetAllInjuries()
ResetPedMovementClipset(cache.ped, 0.0)
TriggerServerEvent('hud:server:RelieveStress', 100)
exports.qbx_core:Notify(locale('info.healthy'), 'inform')
LocalPlayer.state.invBusy = false
end)