Merge branch 'esx-framework:main' into main

This commit is contained in:
Mathu-lmn
2022-08-07 11:58:17 +02:00
committed by GitHub
13 changed files with 577 additions and 361 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ function Async.parallelLimit(tasks, limit, cb)
local queue, results = {}, {}
for i=1, #tasks, 1 do
queue[queue + 1] = tasks[i]
queue[#queue + 1] = tasks[i]
end
local function processQueue()
+1
View File
@@ -0,0 +1 @@
PREVIEW_KEYBIND = false -- "LMENU"
+1
View File
@@ -11,6 +11,7 @@ ui_page 'index.html'
shared_script '@es_extended/imports.lua'
client_scripts {
'config.lua',
'main.lua',
}
+8 -3
View File
@@ -3,6 +3,7 @@
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -286,9 +287,13 @@
})
window.addEventListener("keydown",(e) => {
if (e.key == "Escape" || e.key == "Backspace") {
Close()
}
if (e.key == "Escape" || e.key == "Backspace") {
if (e.target.tagName.toLowerCase() == "input") {
return
}
Close()
}
})
// Open([
+68 -45
View File
@@ -20,6 +20,8 @@ function Open(position,eles,onSelect,onClose)
onClose = onClose
}
LocalPlayer.state:set("context:active",true)
Post("Open",eles,position)
end
@@ -31,6 +33,8 @@ function Closed()
activeMenu = nil
LocalPlayer.state:set("context:active",false)
if cb then
cb(menu)
end
@@ -95,7 +99,7 @@ RegisterNUICallback("selected",function(data)
return
end
activeMenu.onSelect(activeMenu,ele)
activeMenu:onSelect(ele)
end)
RegisterNUICallback("changed",function(data)
@@ -130,62 +134,81 @@ RegisterNUICallback("changed",function(data)
end
end)
-- Keybind
local function focusPreview()
if not activeMenu
or not activeMenu.onSelect
then
return
end
SetNuiFocus(true,true)
end
if PREVIEW_KEYBIND then
RegisterCommand("previewContext",focusPreview)
RegisterKeyMapping("previewContext","Preview Active Context","keyboard",PREVIEW_KEYBIND)
end
exports("focusPreview",focusPreview)
-- Debug/Test
-- Commands:
-- [ ctx:preview | ctx:open | ctx:close | ctx:form ]
local position = "right"
if Debug then
local position = "right"
local eles = {
{
unselectable=true,
icon="fas fa-info-circle",
title="Unselectable Item (Header/Label?)",
},
{
icon="fas fa-check",
title="Item A",
description="Some description here. Add some words to make the text overflow."
},
{
disabled=true,
icon="fas fa-times",
title="Disabled Item",
description="Some description here. Add some words to make the text overflow."
},
{
icon="fas fa-check",
title="Item B",
description="Some description here. Add some words to make the text overflow."
},
}
local eles = {
{
unselectable=true,
icon="fas fa-info-circle",
title="Unselectable Item (Header/Label?)",
},
{
icon="fas fa-check",
title="Item A",
description="Some description here. Add some words to make the text overflow."
},
{
disabled=true,
icon="fas fa-times",
title="Disabled Item",
description="Some description here. Add some words to make the text overflow."
},
{
icon="fas fa-check",
title="Item B",
description="Some description here. Add some words to make the text overflow."
},
}
local function onSelect(menu,ele)
print("Ele selected",ele.title)
local function onSelect(menu,ele)
print("Ele selected",ele.title)
if ele.name == "close" then
exports["esx_context"]:Close()
end
if ele.name ~= "submit" then
return
end
for _,ele in ipairs(menu.eles) do
if ele.input then
print(ele.name,ele.inputType,ele.inputValue)
end
end
if ele.name == "close" then
exports["esx_context"]:Close()
end
if ele.name ~= "submit" then
return
local function onClose(menu)
print("Menu closed.")
end
for _,ele in ipairs(menu.eles) do
if ele.input then
print(ele.name,ele.inputType,ele.inputValue)
end
end
exports["esx_context"]:Close()
end
local function onClose(menu)
print("Menu closed.")
end
if Debug then
RegisterCommand("ctx:preview",function()
exports["esx_context"]:Preview(position,eles)
end)
+45 -17
View File
@@ -1,22 +1,50 @@
local CurrentProgress = nil
local function Progressbar(message,length,Options)
if Options.animation then
if Options.animation.type == "anim" then
ESX.Streaming.RequestAnimDict(Options.animation.dict, function()
TaskPlayAnim(ESX.PlayerData.ped, Options.animation.dict, Options.animation.lib, 1.0, 1.0, length, 1, 1.0, false,false,false)
RemoveAnimDict(Options.animation.dict)
end)
local Canceled = false
if not CurrentProgress then
CurrentProgress = Options or {}
if CurrentProgress.animation then
if CurrentProgress.animation.type == "anim" then
ESX.Streaming.RequestAnimDict(CurrentProgress.animation.dict, function()
TaskPlayAnim(ESX.PlayerData.ped, CurrentProgress.animation.dict, CurrentProgress.animation.lib, 1.0, 1.0, length, 1, 1.0, false,false,false)
RemoveAnimDict(CurrentProgress.animation.dict)
end)
elseif CurrentProgress.animation.type == "Scenario" then
TaskStartScenarioInPlace(ESX.PlayerData.ped, CurrentProgress.animation.Scenario, 0, true)
end
end
if CurrentProgress.FreezePlayer then FreezeEntityPosition(PlayerPedId(),CurrentProgress.FreezePlayer) end
SendNuiMessage(json.encode({
type = "Progressbar",
length = length or 3000,
message = message or "ESX-Framework"
}))
CurrentProgress.length = length or 3000
while CurrentProgress ~= nil do
if CurrentProgress.length > 0 then
CurrentProgress.length -= 1000
else
ClearPedTasks(ESX.PlayerData.ped)
if CurrentProgress.FreezePlayer then FreezeEntityPosition(PlayerPedId(), false) end
if CurrentProgress.onFinish then CurrentProgress.onFinish() end
CurrentProgress = nil
end
Wait(1000)
end
end
if Options.FreezePlayer then FreezeEntityPosition(PlayerPedId(),Options.FreezePlayer) end
SendNuiMessage(json.encode({
type = "Progressbar",
length = length or 3000,
message = message or "ESX-Framework"
}))
Wait(length)
if Options.FreezePlayer then FreezeEntityPosition(PlayerPedId(),not Options.FreezePlayer) end
if Options.onFinish then Options.onFinish() end
end
exports('Progressbar', Progressbar)
ESX.RegisterInput("cancelprog", "[ProgressBar] Cancel Progressbar", "keyboard", "BACK", function()
if not CurrentProgress then return end
if not CurrentProgress.onCancel then return end
SendNuiMessage(json.encode({
type = "Close"
}))
ClearPedTasks(ESX.PlayerData.ped)
if CurrentProgress.FreezePlayer then FreezeEntityPosition(PlayerPedId(), false) end
CurrentProgress.canceled = true
CurrentProgress.length = 0
CurrentProgress.onCancel()
CurrentProgress = nil
end)
exports('Progressbar', Progressbar)
+1
View File
@@ -1,6 +1,7 @@
fx_version 'adamant'
game 'gta5'
author 'ESX-Framework'
lua54 'yes'
description 'ESX Progressbar'
client_scripts { 'Progress.lua' }
+39 -3
View File
@@ -1,13 +1,46 @@
const w = window
const doc = document
const codes = {
"~r~": "red",
"~b~": "#378cbf",
"~g~": "green",
"~y~": "yellow",
"~p~": "purple",
"~c~": "grey",
"~m~": "#212121",
"~u~": "black",
"~o~": "orange"
}
$(function () {
w.addEventListener('message', function(e) {
$(".text").text(e.data.message)
if (e.data.type === "Progressbar") {
var message = e.data.message
const replaceColors = (str, obj) => {
let strToReplace = str
for (let id in obj) {
strToReplace = strToReplace.replace(new RegExp(id, 'g'), obj[id])
}
return strToReplace
}
for (color in codes) {
if (message.includes(color)) {
let objArr = {};
objArr[color] = `<span style="color: ${codes[color]}">`;
objArr["~s~"] = "</span>";
let newStr = replaceColors(message, objArr);
message = newStr;
}
}
$(".text").text(message)
doc.getElementById("notifyInfo").style.display = "block";
const start = new Date();
const maxTime = e.data.length
const maxTime = e.data.length;
const timeoutValue = Math.floor(maxTime/100);
animUpdate();
@@ -21,13 +54,16 @@ $(function () {
const prc = Math.round((timeoutDiff/maxTime)*100);
if (prc <= 100) {
updateProg(prc);
setTimeout(animUpdate, timeoutValue);
this.timeoutID = setTimeout(animUpdate, timeoutValue);
} else {
doc.getElementById('notifyInfo').style.display = "none";
}
}
} else {
doc.getElementById("notifyInfo").style.display = "none"
clearTimeout(this.timeoutID);
timeoutValue = 0;
}
})
})
+37 -2
View File
@@ -16,6 +16,7 @@
```
* Export
```lua
exports["esx_progressbar"]:Progressbar("Unlocking Storage", 3000,{
FreezePlayer = true,
@@ -27,5 +28,39 @@
onFinish = function()
--Code here
end})
```
```
* Cancel
```lua
ESX.Progressbar("Unlocking Storage", 3000,{
FreezePlayer = true,
animation ={
type = "anim",
dict = "anim@mp_player_intmenu@key_fob@",
lib ="fob_click"
},
onFinish = function()
--Code here
end, onCancel = function()
--Code here
end
})
```
* Scenario
```lua
ESX.Progressbar("Unlocking Storage", 3000,{
FreezePlayer = true,
animation ={
type = "Scenario",
Scenario = "PROP_HUMAN_BUM_BIN",
},
onFinish = function()
--Code here
end, onCancel = function()
--Code here
end
})
```
+364 -261
View File
@@ -4,375 +4,478 @@ isDead, isSearched, medic = false, false, 0
RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(xPlayer)
ESX.PlayerLoaded = true
ESX.PlayerLoaded = true
end)
RegisterNetEvent('esx:onPlayerLogout')
AddEventHandler('esx:onPlayerLogout', function()
ESX.PlayerLoaded = false
firstSpawn = true
ESX.PlayerLoaded = false
firstSpawn = true
end)
AddEventHandler('esx:onPlayerSpawn', function()
isDead = false
isDead = false
ClearTimecycleModifier()
SetPedMotionBlur(PlayerPedId(), false)
ClearExtraTimecycleModifier()
EndDeathCam()
if firstSpawn then
firstSpawn = false
if firstSpawn then
firstSpawn = false
if Config.SaveDeathStatus then
while not ESX.PlayerLoaded do
Wait(1000)
end
if Config.SaveDeathStatus then
while not ESX.PlayerLoaded do
Wait(1000)
end
ESX.TriggerServerCallback('esx_ambulancejob:getDeathStatus', function(shouldDie)
if shouldDie then
Wait(1000)
SetEntityHealth(PlayerPedId(), 0)
end
end)
end
end
ESX.TriggerServerCallback('esx_ambulancejob:getDeathStatus', function(shouldDie)
if shouldDie then
Wait(1000)
SetEntityHealth(PlayerPedId(), 0)
end
end)
end
end
end)
-- Create blips
CreateThread(function()
for k,v in pairs(Config.Hospitals) do
local blip = AddBlipForCoord(v.Blip.coords)
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)
SetBlipSprite(blip, v.Blip.sprite)
SetBlipScale(blip, v.Blip.scale)
SetBlipColour(blip, v.Blip.color)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName('STRING')
AddTextComponentSubstringPlayerName(_U('blip_hospital'))
EndTextCommandSetBlipName(blip)
end
BeginTextCommandSetBlipName('STRING')
AddTextComponentSubstringPlayerName(_U('blip_hospital'))
EndTextCommandSetBlipName(blip)
end
while true do
local Sleep = 1500
if isDead then
Sleep = 0
DisableAllControlActions(0)
EnableControlAction(0, 47, true) -- G
EnableControlAction(0, 245, true) -- T
EnableControlAction(0, 38, true) -- E
while true do
local Sleep = 1500
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
end
if isDead then
Sleep = 0
DisableAllControlActions(0)
EnableControlAction(0, 47, true) -- G
EnableControlAction(0, 245, true) -- T
EnableControlAction(0, 38, true) -- E
Wait(Sleep)
end
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
end
Wait(Sleep)
end
end)
RegisterNetEvent('esx_ambulancejob:clsearch')
AddEventHandler('esx_ambulancejob:clsearch', function(medicId)
local playerPed = PlayerPedId()
local playerPed = PlayerPedId()
if isDead then
local coords = GetEntityCoords(playerPed)
local playersInArea = ESX.Game.GetPlayersInArea(coords, 50.0)
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
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()
isDead = true
ESX.UI.Menu.CloseAll()
ESX.HideUI()
TriggerServerEvent('esx_ambulancejob:setDeathStatus', true)
StartDeathTimer()
StartDistressSignal()
isDead = true
ESX.UI.Menu.CloseAll()
ClearTimecycleModifier()
SetTimecycleModifier("REDMIST_blend")
SetTimecycleModifierStrength(0.7)
SetExtraTimecycleModifier("fp_vig_red")
SetExtraTimecycleModifierStrength(1.0)
SetPedMotionBlur(PlayerPedId(), true)
TriggerServerEvent('esx_ambulancejob:setDeathStatus', true)
StartDeathTimer()
StartDeathCam()
StartDistressSignal()
end
RegisterNetEvent('esx_ambulancejob:useItem')
AddEventHandler('esx_ambulancejob:useItem', function(itemName)
ESX.UI.Menu.CloseAll()
ESX.UI.Menu.CloseAll()
if itemName == 'medikit' then
local lib, anim = 'anim@heists@narcotics@funding@gang_idle', 'gang_chatting_idle01' -- TODO better animations
local playerPed = PlayerPedId()
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)
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
Wait(500)
while IsEntityPlayingAnim(playerPed, lib, anim, 3) do
Wait(0)
DisableAllControlActions(0)
end
TriggerEvent('esx_ambulancejob:heal', 'big', true)
ESX.ShowNotification(_U('used_medikit'))
end)
TriggerEvent('esx_ambulancejob:heal', 'big', true)
ESX.ShowNotification(_U('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()
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)
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
Wait(500)
while IsEntityPlayingAnim(playerPed, lib, anim, 3) do
Wait(0)
DisableAllControlActions(0)
end
TriggerEvent('esx_ambulancejob:heal', 'small', true)
ESX.ShowNotification(_U('used_bandage'))
end)
end
TriggerEvent('esx_ambulancejob:heal', 'small', true)
ESX.ShowNotification(_U('used_bandage'))
end)
end
end)
function StartDistressSignal()
CreateThread(function()
local timer = Config.BleedoutTimer
CreateThread(function()
local timer = Config.BleedoutTimer
while timer > 0 and isDead do
Wait(0)
timer = timer - 30
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(_U('distress_send'))
EndTextCommandDisplayText(0.43, 0.77)
SetTextFont(4)
SetTextScale(0.5, 0.5)
SetTextColour(200, 50, 50, 255)
SetTextDropshadow(0.1, 3, 27, 27, 255)
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(_U('distress_send'))
EndTextCommandDisplayText(0.43, 0.77)
if IsControlJustReleased(0, 47) then
SendDistressSignal()
break
end
end
end)
if IsControlJustReleased(0, 47) then
SendDistressSignal()
break
end
end
end)
end
function SendDistressSignal()
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
ESX.ShowNotification(_U('distress_sent'))
TriggerServerEvent('esx_ambulancejob:onPlayerDistress')
ESX.ShowNotification(_U('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)
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
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))
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
return mins, secs
end
end
function StartDeathTimer()
local canPayFine = false
local canPayFine = false
if Config.EarlyRespawnFine then
ESX.TriggerServerCallback('esx_ambulancejob:checkBalance', function(canPay)
canPayFine = canPay
end)
end
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)
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)
CreateThread(function()
-- early respawn timer
while earlySpawnTimer > 0 and isDead do
Wait(1000)
if earlySpawnTimer > 0 then
earlySpawnTimer = earlySpawnTimer - 1
end
end
if earlySpawnTimer > 0 then
earlySpawnTimer = earlySpawnTimer - 1
end
end
-- bleedout timer
while bleedoutTimer > 0 and isDead do
Wait(1000)
-- bleedout timer
while bleedoutTimer > 0 and isDead do
Wait(1000)
if bleedoutTimer > 0 then
bleedoutTimer = bleedoutTimer - 1
end
end
end)
if bleedoutTimer > 0 then
bleedoutTimer = bleedoutTimer - 1
end
end
end)
CreateThread(function()
local text, timeHeld
CreateThread(function()
local text, timeHeld
-- early respawn timer
while earlySpawnTimer > 0 and isDead do
Wait(0)
text = _U('respawn_available_in', secondsToClock(earlySpawnTimer))
-- early respawn timer
while earlySpawnTimer > 0 and isDead do
Wait(0)
text = _U('respawn_available_in', secondsToClock(earlySpawnTimer))
DrawGenericTextThisFrame()
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(0.5, 0.8)
end
DrawGenericTextThisFrame()
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(0.5, 0.8)
end
-- bleedout timer
while bleedoutTimer > 0 and isDead do
Wait(0)
text = _U('respawn_bleedout_in', secondsToClock(bleedoutTimer))
-- bleedout timer
while bleedoutTimer > 0 and isDead do
Wait(0)
text = _U('respawn_bleedout_in', secondsToClock(bleedoutTimer))
if not Config.EarlyRespawnFine then
text = text .. _U('respawn_bleedout_prompt')
if not Config.EarlyRespawnFine then
text = text .. _U('respawn_bleedout_prompt')
if IsControlPressed(0, 38) and timeHeld > 120 then
RemoveItemsAfterRPDeath()
break
end
elseif Config.EarlyRespawnFine and canPayFine then
text = text .. _U('respawn_bleedout_fine', ESX.Math.GroupDigits(Config.EarlyRespawnFineAmount))
if IsControlPressed(0, 38) and timeHeld > 120 then
RemoveItemsAfterRPDeath()
break
end
elseif Config.EarlyRespawnFine and canPayFine then
text = text .. _U('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) and timeHeld > 120 then
TriggerServerEvent('esx_ambulancejob:payFine')
RemoveItemsAfterRPDeath()
break
end
end
if IsControlPressed(0, 38) then
timeHeld += 1
else
timeHeld = 0
end
if IsControlPressed(0, 38) then
timeHeld += 1
else
timeHeld = 0
end
DrawGenericTextThisFrame()
DrawGenericTextThisFrame()
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(0.5, 0.8)
end
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandDisplayText(0.5, 0.8)
end
if bleedoutTimer < 1 and isDead then
RemoveItemsAfterRPDeath()
end
end)
if bleedoutTimer < 1 and isDead then
RemoveItemsAfterRPDeath()
end
end)
end
function GetClosestRespawnPoint()
local PlyCoords = GetEntityCoords(PlayerPedId())
local ClosestDist, ClosestHospital, ClosestCoord = 10000, {}, nil
local PlyCoords = GetEntityCoords(PlayerPedId())
local ClosestDist, ClosestHospital, ClosestCoord = 10000, {}, nil
for k,v in pairs(Config.RespawnPoints) do
local Distance = #(PlyCoords - vector3(v.coords.x, v.coords.y, v.coords.z))
if Distance <= ClosestDist then
ClosestDist = Distance
ClosestHospital = v
ClosestCoord = vector3(v.coords.x, v.coords.y, v.coords.z)
end
end
for k, v in pairs(Config.RespawnPoints) do
local Distance = #(PlyCoords - vector3(v.coords.x, v.coords.y, v.coords.z))
if Distance <= ClosestDist then
ClosestDist = Distance
ClosestHospital = v
ClosestCoord = vector3(v.coords.x, v.coords.y, v.coords.z)
end
end
return ClosestCoord, ClosestHospital
return ClosestCoord, ClosestHospital
end
function RemoveItemsAfterRPDeath()
TriggerServerEvent('esx_ambulancejob:setDeathStatus', false)
TriggerServerEvent('esx_ambulancejob:setDeathStatus', false)
CreateThread(function()
ESX.TriggerServerCallback('esx_ambulancejob:removeItemsAfterRPDeath', function()
local RespawnCoords, ClosestHospital = GetClosestRespawnPoint()
ESX.SetPlayerData('loadout', {})
CreateThread(function()
ESX.TriggerServerCallback('esx_ambulancejob:removeItemsAfterRPDeath', function()
local RespawnCoords, ClosestHospital = GetClosestRespawnPoint()
DoScreenFadeOut(800)
RespawnPed(PlayerPedId(), RespawnCoords, ClosestHospital.heading)
while not IsScreenFadedOut() do
Wait(0)
end
DoScreenFadeIn(800)
end)
end)
ESX.SetPlayerData('loadout', {})
DoScreenFadeOut(800)
RespawnPed(PlayerPedId(), RespawnCoords, 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)
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)
TriggerServerEvent('esx:onPlayerSpawn')
TriggerEvent('esx:onPlayerSpawn')
TriggerEvent('playerSpawned') -- compatibility with old scripts, will be removed soon
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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'
}
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)
TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon)
end)
AddEventHandler('esx:onPlayerDeath', function(data)
OnPlayerDeath()
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)
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
TriggerServerEvent('esx_ambulancejob:setDeathStatus', false)
while not IsScreenFadedOut() do
Wait(50)
end
DoScreenFadeOut(800)
local formattedCoords = {
x = ESX.Math.Round(coords.x, 1),
y = ESX.Math.Round(coords.y, 1),
z = ESX.Math.Round(coords.z, 1)
}
while not IsScreenFadedOut() do
Wait(50)
end
RespawnPed(playerPed, formattedCoords, 0.0)
isDead = false
DoScreenFadeIn(800)
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
RequestIpl('Coroner_Int_on') -- Morgue
end
local cam = nil
local isDead = false
local angleY = 0.0
local angleZ = 0.0
-------------------------------------------------------
-----------------DEATH CAMERA FUNCTIONS ---------------
--------------------------------------------------------
-- initialize camera
function StartDeathCam()
ClearFocus()
local playerPed = PlayerPedId()
cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", GetEntityCoords(playerPed), 0, 0, 0, GetGameplayCamFov())
SetCamActive(cam, true)
RenderScriptCams(true, true, 1000, true, false)
end
-- destroy camera
function EndDeathCam()
ClearFocus()
RenderScriptCams(false, false, 0, true, false)
DestroyCam(cam, false)
cam = nil
end
-- process camera controls
function ProcessCamControls()
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
-- disable 1st person as the 1st person camera can cause some glitches
DisableFirstPersonCamThisFrame()
-- calculate new position
local newPos = ProcessNewPosition()
SetFocusArea(newPos.x, newPos.y, newPos.z, 0.0, 0.0, 0.0)
-- set coords of cam
SetCamCoord(cam, newPos.x, newPos.y, newPos.z)
-- set rotation
PointCamAtCoord(cam, playerCoords.x, playerCoords.y, playerCoords.z + 0.5)
end
function ProcessNewPosition()
local mouseX = 0.0
local mouseY = 0.0
-- keyboard
if (IsInputDisabled(0)) then
-- rotation
mouseX = GetDisabledControlNormal(1, 1) * 8.0
mouseY = GetDisabledControlNormal(1, 2) * 8.0
-- controller
else
mouseX = GetDisabledControlNormal(1, 1) * 1.5
mouseY = GetDisabledControlNormal(1, 2) * 1.5
end
angleZ = angleZ - mouseX -- around Z axis (left / right)
angleY = angleY + mouseY -- up / down
-- limit up / down angle to 90°
if (angleY > 89.0) then
angleY = 89.0
elseif (angleY < -89.0) then
angleY = -89.0
end
local pCoords = GetEntityCoords(PlayerPedId())
local behindCam = {x = pCoords.x + ((Cos(angleZ) * Cos(angleY)) + (Cos(angleY) * Cos(angleZ))) / 2 * (5.5 + 0.5),
y = pCoords.y + ((Sin(angleZ) * Cos(angleY)) + (Cos(angleY) * Sin(angleZ))) / 2 * (5.5 + 0.5),
z = pCoords.z + ((Sin(angleY))) * (5.5 + 0.5)}
local rayHandle = StartShapeTestRay(pCoords.x, pCoords.y, pCoords.z + 0.5, behindCam.x, behindCam.y, behindCam.z, -1, PlayerPedId(), 0)
local a, hitBool, hitCoords, surfaceNormal, entityHit = GetShapeTestResult(rayHandle)
local maxRadius = 1.9
if (hitBool and Vdist(pCoords.x, pCoords.y, pCoords.z + 0.5, hitCoords) < 5.5 + 0.5) then
maxRadius = Vdist(pCoords.x, pCoords.y, pCoords.z + 0.5, hitCoords)
end
local offset = {x = ((Cos(angleZ) * Cos(angleY)) + (Cos(angleY) * Cos(angleZ))) / 2 * maxRadius,
y = ((Sin(angleZ) * Cos(angleY)) + (Cos(angleY) * Sin(angleZ))) / 2 * maxRadius, z = ((Sin(angleY))) * maxRadius}
local pos = {x = pCoords.x + offset.x, y = pCoords.y + offset.y, z = pCoords.z + offset.z}
return pos
end
+4 -14
View File
@@ -479,6 +479,7 @@ end)
CreateThread(function()
while true do
local Sleep = 1500
local Near = false
local playerPed = PlayerPedId()
if IsPedInAnyVehicle(playerPed, false) then
@@ -488,14 +489,14 @@ CreateThread(function()
if (ESX.PlayerData.job and ESX.PlayerData.job.name == 'mechanic') or not Config.IsMechanicJobOnly then
for k, v in pairs(Config.Zones) do
local zonePos = vector3(v.Pos.x, v.Pos.y, v.Pos.z)
if #(coords - zonePos) < v.Size.x then
if #(coords - zonePos) < 10.0 then
Near = true
Sleep = 0
if not lsMenuIsShowed then
if not HintDisplayed then
HintDisplayed = true
ESX.TextUI(v.Hint)
end
if IsControlJustReleased(0, 38) then
lsMenuIsShowed = true
@@ -527,21 +528,10 @@ CreateThread(function()
end
end)
end
else
if HintDisplayed then
HintDisplayed = false
ESX.HideUI()
end
end
else
if HintDisplayed then
HintDisplayed = false
ESX.HideUI()
end
end
end
else
if HintDisplayed then
if not Near and HintDisplayed then
HintDisplayed = false
ESX.HideUI()
end
+1 -1
View File
@@ -4,7 +4,7 @@ Locales['en'] = {
['already_own'] = 'you already own: %s',
['not_enough_money'] = 'you cannot afford this mod!',
['purchased'] = 'you have purchased the mod!',
['press_custom'] = 'press ~INPUT_PICKUP~ to personalize your vehicle.',
['press_custom'] = 'press ~b~[E]~s~ to Personalise Your Vehicle',
['level'] = 'level %s',
['neon'] = 'neon',
['blip_name'] = 'los Santos Customs',
+7 -14
View File
@@ -1,24 +1,17 @@
<h1 align='center'>ESX Legacy</a></h1><p align='center'><b><a href='https://discord.esx-framework.org/'>Discord</a> - <a href='https://esx-framework.org/'>Website</a> - <a href='https://docs.esx-framework.org/legacy/installation'>Documentation</a></b></h5>
<h1 align='center'>ESX Legacy</a></h1>
<p align="center"><img src="https://docs.esx-framework.org/severcount.svg"/></p>
<p align='center'><b><a href='https://discord.esx-framework.org/'>Discord</a> - <a href='https://esx-framework.org/'>Website</a> - <a href='https://docs.esx-framework.org/legacy/installation'>Documentation</a></b></h5>
**IMPORTANT**: ESX Legacy now supports ox Inventory. **HOWEVER, THIS SHOULD ONLY BE USED IF YOU ARE EXPERIENCED AND KNOW WHAT YOU ARE DOING**.
##### ESX is the leading framework for creating roleplay servers on FiveM, with many official and community resources designed to utilise the tools provided here. For a taste of what's available
> esx_identity: Enables character registration defining a players name, sex, height, and date of birth
> esx_society: Allows job resources to register a society, gaining employee management, society funds, and more
> esx_billing: Allows members of some societies to send fines or bills to other players
> esx_vehicleshop: Allow players to purchase vehicles from a dealership, or setup society support for a player-managed dealership
> esx_ambulancejob: Adds a death and respawn system while allowing players to work as EMS to heal and revive others
Many more resources are included in this repository, or you can browse the [ESX Community Github](https://github.com/esx-community/) or [Cfx.re Releases board](https://forum.cfx.re/tag/esx) for more.
<p align='center'>Want More resources? You can browse the <a href="https://github.com/esx-community/">ESX Community Github</a> or <a href="https://forum.cfx.re/tag/esx">Cfx.re Releases board</a> for more!
<p align='center'><b>ESX is the leading framework, trusted By thousands of commmunitys for the heighest quality roleplay servers on FiveM</b></p>
<hr>
### License
es_extended - ESX framework for FiveM
ESX-legacy - ESX framework for FiveM
Copyright (C) 2015-2022 Jérémie N'gadi
Copyright (C) 2015-2022 ESX-Framework
This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version.