397 lines
14 KiB
Lua
397 lines
14 KiB
Lua
local firstSpawn = true
|
|
isDead, isSearched, medic = false, false, 0
|
|
|
|
RegisterNetEvent('esx:playerLoaded')
|
|
AddEventHandler('esx:playerLoaded', function(xPlayer)
|
|
ESX.PlayerLoaded = true
|
|
end)
|
|
|
|
RegisterNetEvent('esx:onPlayerLogout')
|
|
AddEventHandler('esx:onPlayerLogout', function()
|
|
ESX.PlayerLoaded = false
|
|
firstSpawn = true
|
|
end)
|
|
|
|
AddEventHandler('esx:onPlayerSpawn', function()
|
|
if firstSpawn then
|
|
firstSpawn = false
|
|
return
|
|
end
|
|
isDead = false
|
|
ClearTimecycleModifier()
|
|
SetPedMotionBlur(PlayerPedId(), false)
|
|
ClearExtraTimecycleModifier()
|
|
EndDeathCam()
|
|
end)
|
|
|
|
-- Create blips
|
|
CreateThread(function()
|
|
for k, v in pairs(Config.Hospitals) do
|
|
local blip = AddBlipForCoord(v.Blip.coords)
|
|
|
|
SetBlipSprite(blip, v.Blip.sprite)
|
|
SetBlipScale(blip, v.Blip.scale)
|
|
SetBlipColour(blip, v.Blip.color)
|
|
SetBlipAsShortRange(blip, true)
|
|
|
|
BeginTextCommandSetBlipName('STRING')
|
|
AddTextComponentSubstringPlayerName(TranslateCap('blip_hospital'))
|
|
EndTextCommandSetBlipName(blip)
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('esx_ambulancejob:clsearch')
|
|
AddEventHandler('esx_ambulancejob:clsearch', function(medicId)
|
|
local playerPed = PlayerPedId()
|
|
|
|
if isDead then
|
|
local coords = GetEntityCoords(playerPed)
|
|
local playersInArea = ESX.Game.GetPlayersInArea(coords, 50.0)
|
|
|
|
for i = 1, #playersInArea, 1 do
|
|
local player = playersInArea[i]
|
|
if player == GetPlayerFromServerId(medicId) then
|
|
medic = tonumber(medicId)
|
|
isSearched = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
function OnPlayerDeath()
|
|
ESX.CloseContext()
|
|
ClearTimecycleModifier()
|
|
SetTimecycleModifier("REDMIST_blend")
|
|
SetTimecycleModifierStrength(0.7)
|
|
SetExtraTimecycleModifier("fp_vig_red")
|
|
SetExtraTimecycleModifierStrength(1.0)
|
|
SetPedMotionBlur(PlayerPedId(), true)
|
|
TriggerServerEvent('esx_ambulancejob:setDeathStatus', true)
|
|
StartDeathTimer()
|
|
StartDeathCam()
|
|
isDead = true
|
|
StartDeathLoop()
|
|
StartDistressSignal()
|
|
|
|
if Config.DeathAnim.enabled then
|
|
local coords = GetEntityCoords(ESX.PlayerData.ped)
|
|
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, 0.0, 0.0, false)
|
|
ESX.Streaming.RequestAnimDict(Config.DeathAnim.dict)
|
|
TaskPlayAnim(ESX.PlayerData.ped, Config.DeathAnim.dict, Config.DeathAnim.name, Config.DeathAnim.fadeIn, Config.DeathAnim.fadeOut,
|
|
-1, Config.DeathAnim.flags, Config.DeathAnim.playbackRate, false, false, false)
|
|
FreezeEntityPosition(ESX.PlayerData.ped, true)
|
|
|
|
Citizen.CreateThreadNow(function()
|
|
while ESX.PlayerData.dead do
|
|
if not IsEntityPlayingAnim(ESX.PlayerData.ped, Config.DeathAnim.dict, Config.DeathAnim.name, 3) then
|
|
TaskPlayAnim(ESX.PlayerData.ped, Config.DeathAnim.dict, Config.DeathAnim.name, Config.DeathAnim.fadeIn, Config.DeathAnim.fadeOut,
|
|
-1, Config.DeathAnim.flags, Config.DeathAnim.playbackRate, false, false, false)
|
|
end
|
|
Wait(0)
|
|
end
|
|
RemoveAnimDict(Config.DeathAnim.dict)
|
|
end)
|
|
|
|
end
|
|
end
|
|
|
|
RegisterNetEvent('esx_ambulancejob:useItem')
|
|
AddEventHandler('esx_ambulancejob:useItem', function(itemName)
|
|
ESX.CloseContext()
|
|
|
|
if itemName == 'medikit' then
|
|
local lib, anim = 'anim@heists@narcotics@funding@gang_idle', 'gang_chatting_idle01' -- TODO better animations
|
|
local playerPed = PlayerPedId()
|
|
|
|
ESX.Streaming.RequestAnimDict(lib, function()
|
|
TaskPlayAnim(playerPed, lib, anim, 8.0, -8.0, -1, 0, 0, false, false, false)
|
|
RemoveAnimDict(lib)
|
|
|
|
Wait(500)
|
|
while IsEntityPlayingAnim(playerPed, lib, anim, 3) do
|
|
Wait(0)
|
|
DisableAllControlActions(0)
|
|
end
|
|
|
|
TriggerEvent('esx_ambulancejob:heal', 'big', true)
|
|
ESX.ShowNotification(TranslateCap('used_medikit'))
|
|
end)
|
|
|
|
elseif itemName == 'bandage' then
|
|
local lib, anim = 'anim@heists@narcotics@funding@gang_idle', 'gang_chatting_idle01' -- TODO better animations
|
|
local playerPed = PlayerPedId()
|
|
|
|
ESX.Streaming.RequestAnimDict(lib, function()
|
|
TaskPlayAnim(playerPed, lib, anim, 8.0, -8.0, -1, 0, 0, false, false, false)
|
|
RemoveAnimDict(lib)
|
|
|
|
Wait(500)
|
|
while IsEntityPlayingAnim(playerPed, lib, anim, 3) do
|
|
Wait(0)
|
|
DisableAllControlActions(0)
|
|
end
|
|
|
|
TriggerEvent('esx_ambulancejob:heal', 'small', true)
|
|
ESX.ShowNotification(TranslateCap('used_bandage'))
|
|
end)
|
|
end
|
|
end)
|
|
|
|
function StartDeathLoop()
|
|
CreateThread(function()
|
|
while isDead do
|
|
DisableAllControlActions(0)
|
|
EnableControlAction(0, 47, true) -- G
|
|
EnableControlAction(0, 245, true) -- T
|
|
EnableControlAction(0, 38, true) -- E
|
|
|
|
ProcessCamControls()
|
|
if isSearched then
|
|
local playerPed = PlayerPedId()
|
|
local ped = GetPlayerPed(GetPlayerFromServerId(medic))
|
|
isSearched = false
|
|
|
|
AttachEntityToEntity(playerPed, ped, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true)
|
|
Wait(1000)
|
|
DetachEntity(playerPed, true, false)
|
|
ClearPedTasksImmediately(playerPed)
|
|
end
|
|
Wait(0)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function StartDistressSignal()
|
|
CreateThread(function()
|
|
local timer = Config.BleedoutTimer
|
|
|
|
while timer > 0 and isDead do
|
|
Wait(0)
|
|
timer = timer - 30
|
|
|
|
SetTextFont(4)
|
|
SetTextScale(0.5, 0.5)
|
|
SetTextColour(200, 50, 50, 255)
|
|
SetTextDropshadow(0.1, 3, 27, 27, 255)
|
|
BeginTextCommandDisplayText('STRING')
|
|
AddTextComponentSubstringPlayerName(TranslateCap('distress_send'))
|
|
EndTextCommandDisplayText(0.446, 0.77)
|
|
|
|
if IsControlJustReleased(0, 47) then
|
|
SendDistressSignal()
|
|
break
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function SendDistressSignal()
|
|
local playerPed = PlayerPedId()
|
|
local coords = GetEntityCoords(playerPed)
|
|
|
|
ESX.ShowNotification(TranslateCap('distress_sent'))
|
|
TriggerServerEvent('esx_ambulancejob:onPlayerDistress')
|
|
end
|
|
|
|
function DrawGenericTextThisFrame()
|
|
SetTextFont(4)
|
|
SetTextScale(0.0, 0.5)
|
|
SetTextColour(255, 255, 255, 255)
|
|
SetTextDropshadow(0, 0, 0, 0, 255)
|
|
SetTextDropShadow()
|
|
SetTextOutline()
|
|
SetTextCentre(true)
|
|
end
|
|
|
|
function secondsToClock(seconds)
|
|
local seconds, hours, mins, secs = tonumber(seconds), 0, 0, 0
|
|
|
|
if seconds <= 0 then
|
|
return 0, 0
|
|
else
|
|
local hours = string.format('%02.f', math.floor(seconds / 3600))
|
|
local mins = string.format('%02.f', math.floor(seconds / 60 - (hours * 60)))
|
|
local secs = string.format('%02.f', math.floor(seconds - hours * 3600 - mins * 60))
|
|
|
|
return mins, secs
|
|
end
|
|
end
|
|
|
|
function StartDeathTimer()
|
|
local canPayFine = false
|
|
|
|
if Config.EarlyRespawnFine then
|
|
ESX.TriggerServerCallback('esx_ambulancejob:checkBalance', function(canPay)
|
|
canPayFine = canPay
|
|
end)
|
|
end
|
|
|
|
local earlySpawnTimer = ESX.Math.Round(Config.EarlyRespawnTimer / 1000)
|
|
local bleedoutTimer = ESX.Math.Round(Config.BleedoutTimer / 1000)
|
|
|
|
CreateThread(function()
|
|
-- early respawn timer
|
|
while earlySpawnTimer > 0 and isDead do
|
|
Wait(1000)
|
|
|
|
if earlySpawnTimer > 0 then
|
|
earlySpawnTimer = earlySpawnTimer - 1
|
|
end
|
|
end
|
|
|
|
-- bleedout timer
|
|
while bleedoutTimer > 0 and isDead do
|
|
Wait(1000)
|
|
|
|
if bleedoutTimer > 0 then
|
|
bleedoutTimer = bleedoutTimer - 1
|
|
end
|
|
end
|
|
end)
|
|
|
|
CreateThread(function()
|
|
local text, timeHeld
|
|
|
|
-- early respawn timer
|
|
while earlySpawnTimer > 0 and isDead do
|
|
Wait(0)
|
|
text = TranslateCap('respawn_available_in', secondsToClock(earlySpawnTimer))
|
|
|
|
DrawGenericTextThisFrame()
|
|
BeginTextCommandDisplayText('STRING')
|
|
AddTextComponentSubstringPlayerName(text)
|
|
EndTextCommandDisplayText(0.5, 0.8)
|
|
end
|
|
|
|
-- bleedout timer
|
|
while bleedoutTimer > 0 and isDead do
|
|
Wait(0)
|
|
text = TranslateCap('respawn_bleedout_in', secondsToClock(bleedoutTimer))
|
|
|
|
if not Config.EarlyRespawnFine then
|
|
text = text .. TranslateCap('respawn_bleedout_prompt')
|
|
|
|
if IsControlPressed(0, 38) and timeHeld > 120 then
|
|
RemoveItemsAfterRPDeath()
|
|
break
|
|
end
|
|
elseif Config.EarlyRespawnFine and canPayFine then
|
|
text = text .. TranslateCap('respawn_bleedout_fine', ESX.Math.GroupDigits(Config.EarlyRespawnFineAmount))
|
|
|
|
if IsControlPressed(0, 38) and timeHeld > 120 then
|
|
TriggerServerEvent('esx_ambulancejob:payFine')
|
|
RemoveItemsAfterRPDeath()
|
|
break
|
|
end
|
|
end
|
|
|
|
if IsControlPressed(0, 38) then
|
|
timeHeld += 1
|
|
else
|
|
timeHeld = 0
|
|
end
|
|
|
|
DrawGenericTextThisFrame()
|
|
|
|
BeginTextCommandDisplayText('STRING')
|
|
AddTextComponentSubstringPlayerName(text)
|
|
EndTextCommandDisplayText(0.5, 0.8)
|
|
end
|
|
|
|
if bleedoutTimer < 1 and isDead then
|
|
RemoveItemsAfterRPDeath()
|
|
end
|
|
end)
|
|
end
|
|
|
|
function GetClosestRespawnPoint()
|
|
local plyCoords = GetEntityCoords(PlayerPedId())
|
|
local closestDist, closestHospital
|
|
|
|
for i=1, #Config.RespawnPoints do
|
|
local dist = #(plyCoords - Config.RespawnPoints[i].coords)
|
|
|
|
if not closestDist or dist <= closestDist then
|
|
closestDist, closestHospital = dist, Config.RespawnPoints[i]
|
|
end
|
|
end
|
|
|
|
return closestHospital
|
|
end
|
|
|
|
function RemoveItemsAfterRPDeath()
|
|
TriggerServerEvent('esx_ambulancejob:setDeathStatus', false)
|
|
|
|
CreateThread(function()
|
|
ESX.TriggerServerCallback('esx_ambulancejob:removeItemsAfterRPDeath', function()
|
|
local ClosestHospital = GetClosestRespawnPoint()
|
|
|
|
ESX.SetPlayerData('loadout', {})
|
|
|
|
DoScreenFadeOut(800)
|
|
RespawnPed(PlayerPedId(), ClosestHospital.coords, ClosestHospital.heading)
|
|
while not IsScreenFadedOut() do
|
|
Wait(0)
|
|
end
|
|
DoScreenFadeIn(800)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
function RespawnPed(ped, coords, heading)
|
|
SetEntityCoordsNoOffset(ped, coords.x, coords.y, coords.z, false, false, false)
|
|
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, heading, true, false)
|
|
SetPlayerInvincible(ped, false)
|
|
ClearPedBloodDamage(ped)
|
|
|
|
if Config.DeathAnim.enabled then
|
|
FreezeEntityPosition(ped, false)
|
|
end
|
|
|
|
TriggerEvent('esx_basicneeds:resetStatus')
|
|
TriggerServerEvent('esx:onPlayerSpawn')
|
|
TriggerEvent('esx:onPlayerSpawn')
|
|
TriggerEvent('playerSpawned') -- compatibility with old scripts, will be removed soon
|
|
end
|
|
|
|
RegisterNetEvent('esx_phone:loaded')
|
|
AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts)
|
|
local specialContact = {name = 'Ambulance', number = 'ambulance',
|
|
base64Icon = 'data:image/png;base64,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'}
|
|
|
|
TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon)
|
|
end)
|
|
|
|
AddEventHandler('esx:onPlayerDeath', function(data)
|
|
OnPlayerDeath()
|
|
end)
|
|
|
|
RegisterNetEvent('esx_ambulancejob:revive')
|
|
AddEventHandler('esx_ambulancejob:revive', function()
|
|
local playerPed = PlayerPedId()
|
|
local coords = GetEntityCoords(playerPed)
|
|
TriggerServerEvent('esx_ambulancejob:setDeathStatus', false)
|
|
|
|
DoScreenFadeOut(800)
|
|
|
|
while not IsScreenFadedOut() do
|
|
Wait(50)
|
|
end
|
|
|
|
local formattedCoords = {x = ESX.Math.Round(coords.x, 1), y = ESX.Math.Round(coords.y, 1), z = ESX.Math.Round(coords.z, 1)}
|
|
|
|
RespawnPed(playerPed, formattedCoords, 0.0)
|
|
isDead = false
|
|
ClearTimecycleModifier()
|
|
SetPedMotionBlur(playerPed, false)
|
|
ClearExtraTimecycleModifier()
|
|
EndDeathCam()
|
|
DoScreenFadeIn(800)
|
|
end)
|
|
|
|
-- Load unloaded IPLs
|
|
if Config.LoadIpl then
|
|
RequestIpl('Coroner_Int_on') -- Morgue
|
|
end
|