1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/jg-hud/framework/cl-functions.lua
2025-08-04 20:26:15 +02:00

382 lines
No EOL
13 KiB
Lua

Framework.CachedPlayerData = {
job = nil,
gang = nil,
cash = nil,
bank = nil,
dirtyMoney = nil,
hunger = nil,
thirst = nil,
stress = nil,
micRange = 2,
radioActive = false,
voiceModes = 3,
}
---@param vehicle integer
---@return number fuelLevel
function Framework.Client.VehicleGetFuel(vehicle)
if not DoesEntityExist(vehicle) then return 0 end
if (Config.FuelSystem == "LegacyFuel" or Config.FuelSystem == "lc_fuel" or Config.FuelSystem == "ps-fuel" or Config.FuelSystem == "lj-fuel" or Config.FuelSystem == "cdn-fuel" or Config.FuelSystem == "hyon_gas_station" or Config.FuelSystem == "okokGasStation" or Config.FuelSystem == "nd_fuel" or Config.FuelSystem == "myFuel") then
return exports[Config.FuelSystem]:GetFuel(vehicle)
elseif Config.FuelSystem == "ti_fuel" then
local level = exports["ti_fuel"]:getFuel(vehicle)
return level
elseif Config.FuelSystem == "ox_fuel" or Config.FuelSystem == "Renewed-Fuel" then
return GetVehicleFuelLevel(vehicle)
elseif Config.FuelSystem == "rcore_fuel" then
return exports.rcore_fuel:GetVehicleFuelPercentage(vehicle)
else
return 100 -- or set up custom fuel system here...
end
end
---Toggles the engine, this is used in the keybind & in the vehicle control panel
---@param vehicle integer
---@param on boolean on or off
function Framework.Client.ToggleEngine(vehicle, on)
-- !! Consider adding a key script check here - to disallow enabling the engine if no keys are present
SetVehicleEngineOn(vehicle, on, false, true)
end
---Code to run when seatbelt is toggled
---Please note; you need Config.UseCustomSeatbeltIntegration = true for this to take effect!
---@param vehicle integer
---@param seatbeltOn boolean true = seatbelt on, false = seatbelt off
function Framework.Client.ToggleSeatbelt(vehicle, seatbeltOn)
if GetResourceState("jim-mechanic") == "started" then
if (seatbeltOn and not exports["jim-mechanic"]:seatBeltOn()) or (seatbeltOn and exports["jim-mechanic"]:seatBeltOn()) then
TriggerEvent("jim-mechanic:client:ToggleSeatbelt")
end
end
-- ...or add another custom seatbelt integration here!
end
---@return table | false playerData
function Framework.Client.GetPlayerData()
if Config.Framework == "QBCore" then
---@diagnostic disable-next-line: undefined-field
return QBCore.Functions.GetPlayerData()
elseif Config.Framework == "Qbox" then
return exports.qbx_core:GetPlayerData()
elseif Config.Framework == "ESX" then
---@diagnostic disable-next-line: undefined-field
return ESX.GetPlayerData()
end
return false
end
---@return string?
function Framework.Client.GetPlayerJob()
local player = Framework.Client.GetPlayerData()
if not player or not player.job then return nil end
if Config.Framework == "QBCore" or Config.Framework == "Qbox" then
return string.format("%s (%s)", player.job.label, player.job.grade.name)
elseif Config.Framework == "ESX" then
return string.format("%s (%s)", player.job.label, player.job.grade_label)
end
return nil
end
---@return string?
function Framework.Client.GetPlayerGang()
if GetResourceState("rcore_gangs") == "started" then
local gang = exports.rcore_gangs:GetPlayerGang()
if not gang then return nil end
return gang.tag
end
local player = Framework.Client.GetPlayerData()
if not player or not player.gang then return nil end
if Config.Framework == "QBCore" or Config.Framework == "Qbox" then
return string.format("%s (%s)", player.gang.label, player.gang.grade.name)
elseif Config.Framework == "ESX" then
return nil
end
return nil
end
---@return boolean dead
function Framework.Client.IsPlayerDead()
if Config.Framework == "QBCore" then
local player = Framework.Client.GetPlayerData()
if not player then return false end
if player.metadata?.isdead or player.metadata?.inlaststand then
return true
end
elseif Config.Framework == "Qbox" then
return LocalPlayer.state.isDead
elseif Config.Framework == "ESX" then
return IsEntityDead(cache.ped)
end
return false
end
---@param type "cash" | "bank" | "money" | "dirtyMoney" | "black_money"
---@return number? balance
function Framework.Client.GetBalance(type)
if Config.Framework == "QBCore" then
---@diagnostic disable-next-line: undefined-field
local playerData = QBCore.Functions.GetPlayerData()
if type == "dirtyMoney" then
-- Fetch "markedbills" from inventory
local dirty = 0
for _, item in pairs(playerData.items or {}) do
if item.name == "markedbills" then
if item.info and item.info.worth then
dirty = dirty + item.info.worth
end
end
end
return dirty
end
return playerData?.money?[type] or 0
elseif Config.Framework == "Qbox" then
if type == "dirtyMoney" and GetResourceState("ox_inventory") == "started" then
-- Fetch dirty_money from ox_inv
return exports.ox_inventory:GetItemCount("black_money") or 0
end
return exports.qbx_core:GetPlayerData()?.money?[type] or 0
elseif Config.Framework == "ESX" then
if type == "cash" then type = "money" end
if type == "dirtyMoney" then type = "black_money" end
---@diagnostic disable-next-line: undefined-field
local playerData = ESX.GetPlayerData()
if not playerData then return 0 end
for i, acc in pairs(playerData?.accounts or {}) do
if acc.name == type then
return acc.money or 0
end
end
return 0
end
return nil
end
---@param vehicle integer
function Framework.Client.GetVehicleMileageInKm(vehicle)
if not vehicle or vehicle == 0 then return false end
-- t1ger_mechanic integration
if GetResourceState("t1ger_mechanic") == "started" then
return exports["t1ger_mechanic"]:GetVehicleMileage(vehicle)
end
-- jim-mechanic integration
if GetResourceState("jim-mechanic") == "started" then
local plate = GetVehicleNumberPlateText(vehicle)
return exports["jim-mechanic"]:GetMilage(plate)
end
-- This is for jg-vehiclemileage (jg-mechanic), as it's stored in a statebag
-- If you're using a different vehicle mileage script, you can add the export for it here! :)
return Entity(vehicle).state.vehicleMileage or 0
end
---@param km number
---@return number
function Framework.Client.ConvertKmToMiles(km)
return km * 0.621371
end
function Framework.Client.ConvertSpeed(speed, unit)
if unit == "mph" then return speed * 2.236936 end
if unit == "kph" then return speed * 3.6 end
return speed
end
function Framework.Client.ConvertDistance(dist, unit)
if unit == "meters" then return dist * 1 end
if unit == "feet" then return dist * 3.28084 end
return dist
end
--
-- From this point on, it's setting up framework event listeners
--
local function updateCachedData(player)
if Config.ShowComponents.job then
Framework.CachedPlayerData.job = Framework.Client.GetPlayerJob()
end
if Config.ShowComponents.gang then
Framework.CachedPlayerData.gang = Framework.Client.GetPlayerGang()
end
if Config.ShowComponents.cashBalance then
Framework.CachedPlayerData.cash = Framework.Client.GetBalance("cash")
end
if Config.ShowComponents.bankBalance then
Framework.CachedPlayerData.bank = Framework.Client.GetBalance("bank")
end
if Config.ShowComponents.dirtyMoneyBalance then
Framework.CachedPlayerData.dirtyMoney = Framework.Client.GetBalance("dirtyMoney")
end
if Config.Framework == "QBCore" and player then
Framework.CachedPlayerData.hunger = lib.math.clamp(player.metadata?.hunger or 0, 0, 100)
Framework.CachedPlayerData.thirst = lib.math.clamp(player.metadata?.thirst or 0, 0, 100)
end
end
function Framework.Client.SetupPlayerLoginListeners()
if Config.Framework == "QBCore" then
RegisterNetEvent("QBCore:Client:OnPlayerLoaded", function()
LocalPlayer.state:set("jgHudPlayerLoggedIn", true)
updateCachedData(Framework.Client.GetPlayerData())
end)
RegisterNetEvent("QBCore:Client:OnPlayerUnload", function()
LocalPlayer.state:set("jgHudPlayerLoggedIn", false)
end)
elseif Config.Framework == "Qbox" then
AddStateBagChangeHandler("isLoggedIn", ("player:%s"):format(cache.serverId), function(_, _, loggedIn)
LocalPlayer.state:set("jgHudPlayerLoggedIn", loggedIn)
end)
elseif Config.Framework == "ESX" then
local function onPlayerLoad()
lib.waitFor(function()
return Framework.Client.GetPlayerData() and cache.ped
end, "Ped has not loaded or GetPlayerData returned false (waited 30 seconds)", 30000)
LocalPlayer.state:set("jgHudPlayerLoggedIn", true)
Framework.CachedPlayerData.job = Framework.Client.GetPlayerJob()
end
RegisterNetEvent("esx:playerLoaded", onPlayerLoad)
RegisterNetEvent("esx:onPlayerSpawn", onPlayerLoad)
RegisterNetEvent("esx:onPlayerLogout", function()
LocalPlayer.state:set("jgHudPlayerLoggedIn", false)
end)
else
-- No framework, just say they are already logged in.
-- Or you can add your own system or exports here! Just update the
-- LocalPlayer jgHudPlayerLoggedIn true/false and everything will work!
lib.waitFor(function()
if cache.ped then
LocalPlayer.state:set("jgHudPlayerLoggedIn", true)
return true
end
end, "[Standalone] Ped never loaded in; could not login (waited 500 seconds)", 500000)
end
end
function Framework.Client.CreateEventListeners()
local state = LocalPlayer.state
Framework.CachedPlayerData.hunger = lib.math.clamp(state.hunger or 0, 0, 100)
Framework.CachedPlayerData.thirst = lib.math.clamp(state.thirst or 0, 0, 100)
Framework.CachedPlayerData.stress = lib.math.clamp(state.stress or 0, 0, 100)
if (Config.Framework == "ESX" or Config.Framework == "QBCore" or Config.Framework == "Qbox") and Framework.Client.GetPlayerData() then
updateCachedData(Framework.Client.GetPlayerData())
end
if Config.Framework == "ESX" then
RegisterNetEvent("esx_status:onTick", function(data)
local newHunger, newThirst
for i = 1, #data do
if data[i].name == "thirst" then newThirst = math.floor(data[i].percent)
elseif data[i].name == "hunger" then newHunger = math.floor(data[i].percent) end
end
Framework.CachedPlayerData.hunger = lib.math.clamp(newHunger or 0, 0, 100)
Framework.CachedPlayerData.thirst = lib.math.clamp(newThirst or 0, 0, 100)
end)
RegisterNetEvent("esx:setJob", function(job)
Framework.CachedPlayerData.job = string.format("%s (%s)", job.label, job.grade_label)
end)
RegisterNetEvent("esx:setAccountMoney", function(account)
if account.name == "money" then Framework.CachedPlayerData.cash = account.money end
if account.name == "bank" then Framework.CachedPlayerData.bank = account.money end
if account.name == "black_money" then Framework.CachedPlayerData.dirtyMoney = account.money end
end)
end
if Config.Framework == "QBCore" or Config.Framework == "Qbox" then
RegisterNetEvent("hud:client:UpdateNeeds", function(newHunger, newThirst)
Framework.CachedPlayerData.hunger = newHunger
Framework.CachedPlayerData.thirst = newThirst
end)
RegisterNetEvent("hud:client:OnMoneyChange", function(type)
-- The reason I fetch them directly from `GetBalance` and don't use the amount/isMinus args is because
-- they didn't consistently work for me - especially when using /setmoney in QB or Qbox
if type == "cash" then Framework.CachedPlayerData.cash = Framework.Client.GetBalance("cash") end
if type == "bank" then Framework.CachedPlayerData.bank = Framework.Client.GetBalance("bank") end
end)
RegisterNetEvent("QBCore:Client:OnJobUpdate", function()
Framework.CachedPlayerData.job = Framework.Client.GetPlayerJob()
end)
RegisterNetEvent("QBCore:Client:OnGangUpdate", function()
Framework.CachedPlayerData.gang = Framework.Client.GetPlayerGang()
end)
end
if Config.Framework == "Qbox" then
local playerState = LocalPlayer.state
Framework.CachedPlayerData.hunger = lib.math.clamp(playerState.hunger or 0, 0, 100)
Framework.CachedPlayerData.thirst = lib.math.clamp(playerState.thirst or 0, 0, 100)
AddEventHandler("ox_inventory:itemCount", function(itemName, totalCount)
if itemName == "black_money" then
Framework.CachedPlayerData.dirtyMoney = totalCount
end
end)
AddStateBagChangeHandler("hunger", ("player:%s"):format(cache.serverId), function(_, _, value)
Framework.CachedPlayerData.hunger = lib.math.clamp(value or 0, 0, 100)
end)
AddStateBagChangeHandler("thirst", ("player:%s"):format(cache.serverId), function(_, _, value)
Framework.CachedPlayerData.thirst = lib.math.clamp(value or 0, 0, 100)
end)
end
-- rcore_gangs
if GetResourceState("rcore_gangs") == "started" then
RegisterNetEvent("rcore_gangs:client:set_gang", function(gang)
Framework.CachedPlayerData.gang = gang?.tag
end)
end
-- jg-stress integration
AddStateBagChangeHandler("stress", ("player:%s"):format(cache.serverId), function(_, _, value)
Framework.CachedPlayerData.stress = lib.math.clamp(value or 0, 0, 100)
end)
-- PMA Voice
if GetResourceState("pma-voice") == "started" then
AddEventHandler("pma-voice:setTalkingMode", function(mode)
Framework.CachedPlayerData.micRange = mode
end)
AddEventHandler("pma-voice:radioActive", function(isActive)
Framework.CachedPlayerData.radioActive = isActive
end)
AddEventHandler("pma-voice:settingsCallback", function(voiceSettings)
Framework.CachedPlayerData.voiceModes = #voiceSettings.voiceModes
end)
end
end