From 2b19223004b3aa5ccfd95271f26305ecfaffd800 Mon Sep 17 00:00:00 2001 From: zRxnx Date: Tue, 17 Oct 2023 02:04:07 +0200 Subject: [PATCH] Convert, Fixes & Features [+] Convert to utility script [+] More Discord logs [+] Navigation system reworked [/] Fixed potential errors --- client/client.lua | 32 +- client/functions.lua | 721 ++++++++++++++++++++++++++++---------- configuration/config.lua | 53 ++- configuration/strings.lua | 90 ++++- configuration/webhook.lua | 31 +- fxmanifest.lua | 5 +- readme.md | 10 +- server/functions.lua | 264 ++++---------- server/server.lua | 299 ++++++++++++---- 9 files changed, 965 insertions(+), 540 deletions(-) diff --git a/client/client.lua b/client/client.lua index 772571c..3fb781e 100644 --- a/client/client.lua +++ b/client/client.lua @@ -1,22 +1,16 @@ -ESX, COOLDOWN, DATA_ROUTE, DATA_CLOTHING = Config.EsxImport(), false, {}, {} +CORE = exports.zrx_utility:GetUtility() +COOLDOWN, DATA_BLIP, DATA_ROUTE, DATA_CLOTHING = false, {}, {}, {} +DATA_ROUTE.coords = {} local GetEntityCoords = GetEntityCoords local SetBlipRoute = SetBlipRoute local RemoveBlip = RemoveBlip local vector3 = vector3 local Wait = Wait -RegisterNetEvent('esx:playerLoaded',function(xPlayer) - ESX.PlayerData = xPlayer -end) - -RegisterNetEvent('esx:setJob', function(job) - ESX.PlayerData.job = job +CORE.Client.RegisterKeyMappingCommand(Config.Command, Strings.cmd_desc, Config.Key, function() + OpenMainMenu() end) -RegisterCommand(Config.Command, function() OpenMainMenu() end) -RegisterKeyMapping(Config.Command, Strings.cmd_desc, 'keyboard', Config.Key) -TriggerEvent('chat:addSuggestion', ('/%s'):format(Config.Command), Strings.cmd_desc, {}) - AddEventHandler('onResourceStop', function(res) if res ~= GetCurrentResourceName() then return end if not Config.Menu.clothe then return end @@ -82,21 +76,17 @@ CreateThread(function() local pedCoords while true do pedCoords = GetEntityCoords(cache.ped) - for k, data in pairs(DATA_ROUTE) do + for k, data in pairs(DATA_BLIP) do if data.time == 0 then - SetBlipRoute(data.blip, false) - RemoveBlip(data.blip) - DATA_ROUTE[k] = nil + RemoveDestionation(k) - Config.Notification(nil, Strings.navi_timeout) + CORE.Bridge.notification(Strings.navi_timeout) elseif #(vector3(data.coords.x, data.coords.y, pedCoords.z) - vector3(pedCoords.x, pedCoords.y, pedCoords.z)) < Config.Navigation.checkDistance then - SetBlipRoute(data.blip, false) - RemoveBlip(data.blip) - DATA_ROUTE[k] = nil + RemoveDestionation(k) - Config.Notification(nil, Strings.navi_reached) + CORE.Bridge.notification(Strings.navi_reached) else - DATA_ROUTE[k].time -= 1 + DATA_BLIP[k].time -= 1 end end diff --git a/client/functions.lua b/client/functions.lua index ca1a6ed..6af8f7b 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -8,7 +8,6 @@ local CascadeShadowsSetDynamicDepthValue = CascadeShadowsSetDynamicDepthValue local CascadeShadowsSetCascadeBoundsScale = CascadeShadowsSetCascadeBoundsScale local SetFlashLightFadeDistance = SetFlashLightFadeDistance local SetLightsCutoffDistanceTweak = SetLightsCutoffDistanceTweak -local DistantCopCarSirens = DistantCopCarSirens local DisableOcclusionThisFrame = DisableOcclusionThisFrame local SetDisableDecalRenderingThisFrame = SetDisableDecalRenderingThisFrame local RemoveParticleFxInRange = RemoveParticleFxInRange @@ -74,6 +73,14 @@ local SetPedPropIndex = SetPedPropIndex local GetPedPropIndex = GetPedPropIndex local GetPedPropTextureIndex = GetPedPropTextureIndex local ClearPedProp = ClearPedProp +local GetStreetNameAtCoord = GetStreetNameAtCoord +local GetStreetNameFromHashKey = GetStreetNameFromHashKey +local ClearGpsMultiRoute = ClearGpsMultiRoute +local StartGpsMultiRoute = StartGpsMultiRoute +local AddPointToGpsMultiRoute = AddPointToGpsMultiRoute +local SetGpsMultiRouteRender = SetGpsMultiRouteRender +local SetBlipRoute = SetBlipRoute +local RemoveBlip = RemoveBlip local CLOTHE_DATA = { ComponentId = { @@ -198,9 +205,6 @@ OpenMainMenu = function() if not Config.CanOpenMenu() then return end local MENU = {} - ESX.UI.Menu.CloseAll() - ESX.CloseContext() - if Config.Menu.player then MENU[#MENU + 1] = { title = Strings.info_title, @@ -332,7 +336,7 @@ OpenInfoMenu = function() local PLAYER_DATA = lib.callback.await('zrx_personalmenu:server:getPlayerData', 500) if COOLDOWN or not PLAYER_DATA?.name then - Config.Notification(nil, Strings.on_cooldown) + CORE.Bridge.notification(Strings.on_cooldown) return OpenMainMenu() end StartCooldown() @@ -343,6 +347,7 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-user', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -351,6 +356,7 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-signature', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -359,6 +365,7 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-calendar-days', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -367,14 +374,16 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-person', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { - title = Strings.sex_title, - description = (Strings.sex_desc):format(ESX.PlayerData.sex == 'm' and Strings.male or Strings.female), + title = Strings.gender_title, + description = (Strings.gender_desc):format(CORE.Bridge.getVariables().sex == 'm' and Strings.male or Strings.female), arrow = false, icon = 'fa-solid fa-genderless', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -383,6 +392,7 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-briefcase', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -391,30 +401,34 @@ OpenInfoMenu = function() arrow = false, icon = 'fa-solid fa-briefcase', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { title = Strings.bank_title, - description = (Strings.bank_desc):format(ESX.Math.GroupDigits(PLAYER_DATA.bank)), + description = (Strings.bank_desc):format(lib.math.groupdigits(PLAYER_DATA.bank, '.')), arrow = false, icon = 'fa-solid fa-credit-card', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { title = Strings.money_title, - description = (Strings.money_desc):format(ESX.Math.GroupDigits(PLAYER_DATA.money)), + description = (Strings.money_desc):format(lib.math.groupdigits(PLAYER_DATA.money, '.')), arrow = false, icon = 'fa-solid fa-coins', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { title = Strings.black_money_title, - description = (Strings.black_money_desc):format(ESX.Math.GroupDigits(PLAYER_DATA.black_money)), + description = (Strings.black_money_desc):format(lib.math.groupdigits(PLAYER_DATA.black_money, '.')), arrow = false, icon = 'fa-solid fa-dollar-sign', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -451,6 +465,7 @@ OpenInfoLicenseMenu = function() arrow = false, icon = 'fa-solid fa-id-card', iconColor = Config.IconColor, + readOnly = true, } end else @@ -460,6 +475,7 @@ OpenInfoLicenseMenu = function() arrow = false, icon = 'fa-solid fa-xmark', iconColor = Config.IconColor, + readOnly = true, } end @@ -841,6 +857,9 @@ OpenVehicleMenu = function() end local MENU = {} + local nearPlayer = lib.getClosestPlayer(GetEntityCoords(cache.ped), 3.0, false) + local nearPlayerId = GetPlayerServerId(nearPlayer) + local nearPlayerPed = GetPlayerPed(nearPlayer) MENU[#MENU + 1] = { title = Strings.eng_title, @@ -866,6 +885,20 @@ OpenVehicleMenu = function() end } + MENU[#MENU + 1] = { + title = Strings.give_title, + description = Strings.give_desc, + arrow = DoesEntityExist(nearPlayerPed) and IsVehicleValid(), + icon = 'fa-solid fa-car', + iconColor = Config.IconColor, + disabled = not DoesEntityExist(nearPlayerPed) or not IsVehicleValid(), + onSelect = function() + local plate = GetVehicleNumberPlateText(cache.vehicle) + + TriggerServerEvent('zrx_personalmenu:server:giveCar', nearPlayerId, plate) + end + } + MENU[#MENU + 1] = { title = Strings.extra_title, description = Strings.extra_desc, @@ -931,44 +964,11 @@ OpenVehicleMenu = function() lib.showContext('zrx_personalmenu:personal_menu:vehicle') end -OpenGiveVehicleMenu = function() - if not IsVehicleValid() then - return OpenMainMenu() - end - - local MENU = {} - local nearPlayer = lib.getClosestPlayer(GetEntityCoords(cache.ped), 3.0, false) - local nearPlayerId = GetPlayerServerId(nearPlayer) - local nearPlayerPed = GetPlayerPed(nearPlayer) - - MENU[#MENU + 1] = { - title = Strings.give_title, - description = Strings.give_desc, - arrow = false, - icon = 'fa-solid fa-car', - iconColor = Config.IconColor, - disabled = not DoesEntityExist(nearPlayerPed), - onSelect = function() - local plate = GetVehicleNumberPlateText(cache.vehicle) - TriggerServerEvent('zrx_personalmenu:server:giveCar', nearPlayerId, plate) - end - } - - lib.registerContext({ - id = 'zrx_personalmenu:personal_menu:give', - title = Strings.menu_veh_give, - options = MENU, - menu = 'zrx_personalmenu:personal_menu:vehicle' - }) - - lib.showContext('zrx_personalmenu:personal_menu:give') -end - OpenVehicleExtrasMenu = function() if not IsVehicleValid() then return OpenMainMenu() elseif not Config.CanOpenExtras() then - Config.Notification(nil, Strings.extra_cannot) + CORE.Bridge.notification(Strings.extra_cannot) return OpenVehicleMenu() end @@ -979,7 +979,7 @@ OpenVehicleExtrasMenu = function() title = (Strings.extra_title2):format(i, IsVehicleExtraTurnedOn(cache.vehicle, i) and Strings.on or Strings.off), description = Strings.extra_desc2, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, disabled = not DoesExtraExist(cache.vehicle, i), args = { id = i }, @@ -1011,22 +1011,34 @@ OpenVehicleLiveryMenu = function() end local MENU = {} + local livCount = GetVehicleLiveryCount(cache.vehicle) + + if livCount > 0 then + for i = 0, livCount do + MENU[#MENU + 1] = { + title = (Strings.livery_title2):format(i), + description = Strings.livery_desc2, + arrow = false, + icon = 'fa-solid fa-car-side', + iconColor = Config.IconColor, + args = { id = i }, + onSelect = function(args) + SetVehicleLivery(cache.vehicle, args.id) - for i = 0, GetVehicleLiveryCount(cache.vehicle) do + OpenVehicleLiveryMenu() + end + } + end + else MENU[#MENU + 1] = { - title = (Strings.livery_title2):format(i), - description = Strings.livery_desc2, + title = 'No liverys', + description = 'This vehicle has no liverys', arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-xmark', iconColor = Config.IconColor, - args = { id = i }, - onSelect = function(args) - SetVehicleLivery(cache.vehicle, args.id) - - OpenVehicleLiveryMenu() - end + readOnly = true, } - end + end lib.registerContext({ id = 'zrx_personalmenu:personal_menu:vehicle:livery', @@ -1124,11 +1136,43 @@ OpenVehicleWindowMenu = function() local MENU = {} + MENU[#MENU + 1] = { + title = Strings.all_win_down_title, + description = Strings.all_win_down_desc, + arrow = false, + icon = 'fa-solid fa-car-side', + iconColor = Config.IconColor, + onSelect = function() + for i = 1, #DATA_WINDOWS do + DATA_WINDOWS[i] = false + end + + RollDownWindows(cache.vehicle) + end + } + + MENU[#MENU + 1] = { + title = Strings.all_win_up_title, + description = Strings.all_win_up_desc, + arrow = false, + icon = 'fa-solid fa-car-side', + iconColor = Config.IconColor, + onSelect = function() + for i = 1, #DATA_WINDOWS do + DATA_WINDOWS[i] = true + end + + for i = 0, 3 do + RollUpWindow(cache.vehicle, i) + end + end + } + MENU[#MENU + 1] = { title = Strings.left_front_win_title, description = Strings.left_front_win_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_WINDOWS.front_left then @@ -1145,7 +1189,7 @@ OpenVehicleWindowMenu = function() title = Strings.right_front_win_title, description = Strings.right_front_win_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_WINDOWS.front_right then @@ -1162,7 +1206,7 @@ OpenVehicleWindowMenu = function() title = Strings.left_back_win_title, description = Strings.left_back_win_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_WINDOWS.back_left then @@ -1179,7 +1223,7 @@ OpenVehicleWindowMenu = function() title = Strings.right_back_win_title, description = Strings.right_back_win_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_WINDOWS.back_right then @@ -1192,38 +1236,6 @@ OpenVehicleWindowMenu = function() end } - MENU[#MENU + 1] = { - title = Strings.all_win_down_title, - description = Strings.all_win_down_desc, - arrow = false, - icon = 'fa-solid fa-car', - iconColor = Config.IconColor, - onSelect = function() - for i = 1, #DATA_WINDOWS do - DATA_WINDOWS[i] = false - end - - RollDownWindows(cache.vehicle) - end - } - - MENU[#MENU + 1] = { - title = Strings.all_win_up_title, - description = Strings.all_win_up_desc, - arrow = false, - icon = 'fa-solid fa-car', - iconColor = Config.IconColor, - onSelect = function() - for i = 1, #DATA_WINDOWS do - DATA_WINDOWS[i] = true - end - - for i = 0, 3 do - RollUpWindow(cache.vehicle, i) - end - end - } - lib.registerContext({ id = 'zrx_personalmenu:personal_menu:vehicle:windows', title = Strings.menu_veh_win, @@ -1249,11 +1261,43 @@ OpenVehicleDoorsMenu = function() local MENU = {} + MENU[#MENU + 1] = { + title = Strings.all_doors_close_title, + description = Strings.all_doors_close_desc, + arrow = false, + icon = 'fa-solid fa-car-side', + iconColor = Config.IconColor, + onSelect = function() + for i = 1, #DATA_DOORS do + DATA_DOORS[i] = true + end + + for i = 0, 4 do + SetVehicleDoorOpen(cache.vehicle, i, false) + end + end + } + + MENU[#MENU + 1] = { + title = Strings.all_doors_open_title, + description = Strings.all_doors_open_desc, + arrow = false, + icon = 'fa-solid fa-car-side', + iconColor = Config.IconColor, + onSelect = function() + for i = 1, #DATA_DOORS do + DATA_DOORS[i] = false + end + + SetVehicleDoorsShut(cache.vehicle) + end + } + MENU[#MENU + 1] = { title = Strings.left_front_door_title, description = Strings.left_front_door_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.front_left then @@ -1270,7 +1314,7 @@ OpenVehicleDoorsMenu = function() title = Strings.right_front_door_title, description = Strings.right_front_door_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.front_right then @@ -1287,7 +1331,7 @@ OpenVehicleDoorsMenu = function() title = Strings.left_back_door_title, description = Strings.left_back_door_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.back_left then @@ -1304,7 +1348,7 @@ OpenVehicleDoorsMenu = function() title = Strings.right_back_door_title, description = Strings.right_back_door_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.back_right then @@ -1321,7 +1365,7 @@ OpenVehicleDoorsMenu = function() title = Strings.hood_title, description = Strings.hood_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.hood then @@ -1338,7 +1382,7 @@ OpenVehicleDoorsMenu = function() title = Strings.trunk_title, description = Strings.trunk_desc, arrow = false, - icon = 'fa-solid fa-car', + icon = 'fa-solid fa-car-side', iconColor = Config.IconColor, onSelect = function() if DATA_DOORS.trunk then @@ -1351,38 +1395,6 @@ OpenVehicleDoorsMenu = function() end } - MENU[#MENU + 1] = { - title = Strings.all_doors_close_title, - description = Strings.all_doors_close_desc, - arrow = false, - icon = 'fa-solid fa-car', - iconColor = Config.IconColor, - onSelect = function() - for i = 1, #DATA_DOORS do - DATA_DOORS[i] = true - end - - for i = 0, 4 do - SetVehicleDoorOpen(cache.vehicle, i, false) - end - end - } - - MENU[#MENU + 1] = { - title = Strings.all_doors_open_title, - description = Strings.all_doors_open_desc, - arrow = false, - icon = 'fa-solid fa-car', - iconColor = Config.IconColor, - onSelect = function() - for i = 1, #DATA_DOORS do - DATA_DOORS[i] = false - end - - SetVehicleDoorsShut(cache.vehicle) - end - } - lib.registerContext({ id = 'zrx_personalmenu:personal_menu:vehicle:doors', title = Strings.menu_veh_doors, @@ -1411,12 +1423,12 @@ OpenSettingMenu = function() if DATA_SETTINGS.graphic then DATA_SETTINGS.graphic = false settingsFile = LoadResourceFile(GetCurrentResourceName(), 'files/graphic_off.dat') - lines = StringSplit(settingsFile, '\n') + lines = CORE.Shared.StringSplit(settingsFile, '\n') for k, v in ipairs(lines) do - if not StartsWith(v, '#') and not StartsWith(v, '//') and (v ~= '' or v ~= ' ') and #v > 1 then + if not CORE.Shared.StartsWith(v, '#') and not CORE.Shared.StartsWith(v, '//') and (v ~= '' or v ~= ' ') and #v > 1 then v = v:gsub('%s+', ' ') - setting = StringSplit(v, ' ') + setting = CORE.Shared.StringSplit(v, ' ') if setting[1] and setting[2] and tonumber(setting[2]) then SetVisualSettingFloat(setting[1], tonumber(setting[2])+.0 ) @@ -1426,12 +1438,12 @@ OpenSettingMenu = function() else DATA_SETTINGS.graphic = true settingsFile = LoadResourceFile(GetCurrentResourceName(), 'files/graphic_on.dat') - lines = StringSplit(settingsFile, '\n') + lines = CORE.Shared.StringSplit(settingsFile, '\n') for k, v in ipairs(lines) do - if not StartsWith(v, '#') and not StartsWith(v, '//') and (v ~= '' or v ~= ' ') and #v > 1 then + if not CORE.Shared.StartsWith(v, '#') and not CORE.Shared.StartsWith(v, '//') and (v ~= '' or v ~= ' ') and #v > 1 then v = v:gsub('%s+', ' ') - setting = StringSplit(v, ' ') + setting = CORE.Shared.StringSplit(v, ' ') if setting[1] and setting[2] and tonumber(setting[2]) then SetVisualSettingFloat(setting[1], tonumber(setting[2])+.0 ) @@ -1466,14 +1478,38 @@ end OpenBillMenu = function() local MENU = {} local PLAYER_BILLS = lib.callback.await('zrx_personalmenu:server:getPlayerBills', 500) + local money = 0 if COOLDOWN then - Config.Notification(nil, Strings.on_cooldown) + CORE.Bridge.notification(Strings.on_cooldown) return OpenMainMenu() end StartCooldown() + for k, data in pairs(PLAYER_BILLS) do + money += data.amount + end + if #PLAYER_BILLS > 0 then + MENU[#MENU + 1] = { + title = 'Informations', + description = 'Hover to see', + arrow = false, + icon = 'fa-solid fa-circle-info', + iconColor = Config.IconColor, + readOnly = true, + metadata = { + { + label = 'Total Bills', + value = ('#%s'):format(#PLAYER_BILLS) + }, + { + label = 'Total Amount', + value = ('$%s'):format(money) + }, + } + } + for k, data in pairs(PLAYER_BILLS) do MENU[#MENU + 1] = { title = (Strings.bill_title):format(k --[[data.id DATABASE ID]]), @@ -1481,16 +1517,26 @@ OpenBillMenu = function() arrow = false, icon = 'fa-solid fa-money-bill', iconColor = Config.IconColor, - args = { id = data.id, label = data.label, amount = data.amount }, + args = { + id = data.id, + label = data.label, + amount = data.amount + }, onSelect = function() COOLDOWN = false - PayBill(data.id) + Config.PayBill(data.id) Wait(100) OpenBillMenu() end, metadata = { - { label = Strings.bill_reason, value = data.label }, - { label = Strings.bill_amount, value = (Strings.bill_amount_value):format(data.amount) }, + { + label = Strings.bill_reason, + value = data.label + }, + { + label = Strings.bill_amount, + value = (Strings.bill_amount_value):format(data.amount) + }, } } end @@ -1501,6 +1547,7 @@ OpenBillMenu = function() arrow = false, icon = 'fa-solid fa-xmark', iconColor = Config.IconColor, + readOnly = true, } end @@ -1516,13 +1563,13 @@ end OpenCompanyMenu = function() local MENU = {} - local DATA_SOCIETY = lib.callback.await('zrx_personalmenu:server:getSocietyData', 500, ESX.PlayerData.job.name) + local DATA_SOCIETY = lib.callback.await('zrx_personalmenu:server:getSocietyData', 500, CORE.Bridge.getVariables().job.name) local nearPlayer = lib.getClosestPlayer(GetEntityCoords(cache.ped), 3.0, false) local nearPlayerId = GetPlayerServerId(nearPlayer) local nearPlayerPed = GetPlayerPed(nearPlayer) if COOLDOWN then - Config.Notification(nil, Strings.on_cooldown) + CORE.Bridge.notification(Strings.on_cooldown) return OpenMainMenu() elseif not IsGradeAllowed() then return OpenMainMenu() @@ -1531,10 +1578,11 @@ OpenCompanyMenu = function() MENU[#MENU + 1] = { title = Strings.company_money_title, - description = (Strings.company_money_desc):format(ESX.Math.GroupDigits(DATA_SOCIETY.money)), + description = (Strings.company_money_desc):format(lib.math.groupdigits(DATA_SOCIETY.money, '.')), arrow = false, icon = 'fa-solid fa-credit-card', iconColor = Config.IconColor, + readOnly = true, } MENU[#MENU + 1] = { @@ -1598,48 +1646,374 @@ end OpenNavigationMenu = function() local MENU = {} + MENU[#MENU + 1] = { + title = Strings.navi_clear, + description = Strings.navi_clear_desc, + arrow = false, + icon = 'fa-solid fa-xmark', + iconColor = Config.IconColor, + disabled = #DATA_BLIP < 1, + onSelect = function() + for k, data in pairs(DATA_BLIP) do + RemoveDestionation(k) + end + + CORE.Bridge.notification(Strings.navi_clear_notify) + end, + } + + MENU[#MENU + 1] = { + title = Strings.navi_preset, + description = Strings.navi_preset_desc, + arrow = true, + icon = 'fa-solid fa-person', + iconColor = Config.IconColor, + disabled = #Config.Navigation.destinations < 1, + onSelect = function() + OpenNavigationPresetMenu() + end + } + + MENU[#MENU + 1] = { + title = Strings.navi_my, + description = Strings.navi_my_desc, + arrow = true, + icon = 'fa-solid fa-person', + iconColor = Config.IconColor, + disabled = #Config.Navigation.destinations < 1, + onSelect = function() + OpenNavigationMyMenu() + end + } + + + lib.registerContext({ + id = 'zrx_personalmenu:personal_menu:navigation', + title = Strings.menu_navi, + options = MENU, + menu = 'zrx_personalmenu:personal_menu:main' + }) + + lib.showContext('zrx_personalmenu:personal_menu:navigation') +end + +OpenNavigationMyMenu = function() + local MENU = {} + local PLAYER_NAVI = lib.callback.await('zrx_personalmenu:server:getPlayerNavigation', 500) + local pedCoords = GetEntityCoords(cache.ped) + local street = GetStreetNameFromHashKey(GetStreetNameAtCoord(pedCoords.x, pedCoords.y, pedCoords.z)) + + MENU[#MENU + 1] = { + title = Strings.navi_create, + description = Strings.navi_create_desc, + arrow = true, + icon = 'fa-solid fa-plus', + iconColor = Config.IconColor, + onSelect = function() + local input = lib.inputDialog(Strings.navi_create_title, { + { + type = 'input', + label = Strings.navi_create_name, + description = Strings.navi_create_name_desc, + required = true, + min = 1, + max = 64 + }, + { + type = 'input', + label = Strings.navi_create_coords, + description = Strings.navi_create_coords_desc, + required = false, + disabled = true, + default = (Strings.navi_create_coords_default):format(CORE.Shared.RoundNumber(pedCoords.x, 1), CORE.Shared.RoundNumber(pedCoords.y, 1), CORE.Shared.RoundNumber(pedCoords.z, 1)) + }, + { + type = 'input', + label = Strings.navi_create_street, + description = Strings.navi_create_street, + required = false, + disabled = true, + default = (Strings.navi_create_street_default):format(street) + }, + }) + + if not input then + CORE.Bridge.notification(Strings.not_fill) + return OpenNavigationMyMenu() + end + + TriggerServerEvent('zrx_personalmenu:server:manageNavigation', 'create', input, vector3(pedCoords.x, pedCoords.y, pedCoords.z), street) + end + } + + if #PLAYER_NAVI > 0 then + for k, data in pairs(PLAYER_NAVI) do + MENU[#MENU + 1] = { + title = data.name, + description = Strings.navi_manage, + arrow = true, + icon = 'fa-solid fa-location-dot', + iconColor = Config.IconColor, + metadata = { + { + label = Strings.navi_street, + value = (Strings.navi_street_desc):format(data.street) + } + }, + args = { + name = data.name, + coords = data.coords, + street = data.street + }, + onSelect = function(args) + OpenNavigationSettingMenu(args) + end, + } + end + else + MENU[#MENU + 1] = { + title = Strings.navi_no, + description = Strings.navi_no_desc, + arrow = false, + icon = 'fa-solid fa-xmark', + iconColor = Config.IconColor, + readOnly = true, + } + end + + lib.registerContext({ + id = 'zrx_personalmenu:personal_menu:navigation:my', + title = Strings.menu_navi_my, + options = MENU, + menu = 'zrx_personalmenu:personal_menu:navigation' + }) + + lib.showContext('zrx_personalmenu:personal_menu:navigation:my') +end + +OpenNavigationSettingMenu = function(data) + local MENU = {} + local pedCoords = GetEntityCoords(cache.ped) + local street = GetStreetNameFromHashKey(GetStreetNameAtCoord(pedCoords.x, pedCoords.y, pedCoords.z)) + + MENU[#MENU + 1] = { + title = Strings.navi_setting, + description = Strings.navi_setting_desc, + arrow = false, + icon = 'fa-solid fa-circle-info', + iconColor = Config.IconColor, + readOnly = true, + metadata = { + { + label = Strings.navi_setting_name, + value = (Strings.navi_setting_name_desc):format(data.name) + }, + { + label = Strings.navi_setting_coords, + value = (Strings.navi_setting_coords_desc):format(CORE.Shared.RoundNumber(data.coords.x, 1), CORE.Shared.RoundNumber(data.coords.y, 1), CORE.Shared.RoundNumber(data.coords.z, 1)) + }, + { + label = Strings.navi_setting_street, + value = (Strings.navi_setting_street_desc):format(data.street) + } + }, + onSelect = function() + OpenNavigationMyMenu() + end + } + + MENU[#MENU + 1] = { + title = Strings.navi_set, + description = Strings.navi_set_desc, + arrow = true, + icon = 'fa-solid fa-location-dot', + iconColor = Config.IconColor, + onSelect = function() + local blip = Config.Navigation.route(vector3(data.coords.x, data.coords.y, data.coords.z), (Strings.navi_dest):format(data.name)) + + DATA_BLIP[#DATA_BLIP + 1] = { + blip = blip, + coords = data.coords, + time = Config.Navigation.timeout + } + + RenderRoute(data.coords) + CORE.Bridge.notification((Strings.navi_set2):format(data.name)) + end, + } + + MENU[#MENU + 1] = { + title = Strings.navi_edit, + description = Strings.navi_edit_desc, + arrow = true, + icon = 'fa-solid fa-pen-to-square', + iconColor = Config.IconColor, + onSelect = function() + local input = lib.inputDialog(Strings.navi_edit_title, { + { + type = 'input', + label = Strings.navi_edit_name, + description = Strings.navi_edit_name_desc, + required = true, + min = 1, + max = 64 + }, + { + type = 'input', + label = Strings.navi_edit_coords, + description = Strings.navi_edit_coords_desc, + required = false, + disabled = true, + default = (Strings.navi_edit_coords_default):format(CORE.Shared.RoundNumber(pedCoords.x, 1), CORE.Shared.RoundNumber(pedCoords.y, 1), CORE.Shared.RoundNumber(pedCoords.z, 1)) + }, + { + type = 'input', + label = Strings.navi_edit_street, + description = Strings.navi_edit_street_desc, + required = false, + disabled = true, + default = (Strings.navi_edit_street_default):format(street) + }, + }) + + if not input then + CORE.Bridge.notification(Strings.not_fill) + return OpenNavigationMyMenu() + end + + TriggerServerEvent('zrx_personalmenu:server:manageNavigation', 'edit', input, vector3(pedCoords.x, pedCoords.y, pedCoords.z), street, { + name = data.name, + coords = data.coords, + street = data.street, + }) + end + } + + MENU[#MENU + 1] = { + title = Strings.navi_delete, + description = Strings.navi_delete_desc, + arrow = true, + icon = 'fa-solid fa-xmark', + iconColor = Config.IconColor, + onSelect = function() + TriggerServerEvent('zrx_personalmenu:server:manageNavigation', 'delete', data.name, data.coords, data.street) + end + } + + lib.registerContext({ + id = 'zrx_personalmenu:personal_menu:navigation:setting', + title = Strings.menu_navi_setting, + options = MENU, + menu = 'zrx_personalmenu:personal_menu:navigation:my' + }) + + lib.showContext('zrx_personalmenu:personal_menu:navigation:setting') +end + +OpenNavigationPresetMenu = function() + local MENU = {} + for k, data in pairs(Config.Navigation.destinations) do + local disable = false + + for v, data2 in pairs(DATA_BLIP) do + if data.coords == data2.coords then + disable = true + end + end + MENU[#MENU + 1] = { - title = data.label, + title = data.name, description = Strings.navi_desc2, arrow = false, icon = data.icon or 'fa-solid fa-location-dot', iconColor = Config.IconColor, - args = { label = data.label }, + disabled = disable, + metadata = { + { + label = Strings.navi_street, + value = (Strings.navi_street_desc):format(GetStreetNameFromHashKey(GetStreetNameAtCoord(data.coords.x, data.coords.y, data.coords.z))) + } + }, + args = { + name = data.name + }, onSelect = function(args) - local found = false - - for v, data2 in pairs(DATA_ROUTE) do - if data.coords == data2.coords then - found = true - end - end - - if found then - Config.Notification(nil, (Strings.navi_already):format(data.label)) - else - local blip = Config.Navigation.route(vector3(data.coords.x, data.coords.y, data.coords.z), (Strings.navi_dest):format(args.label)) + local blip = Config.Navigation.route(vector3(data.coords.x, data.coords.y, data.coords.z), (Strings.navi_dest):format(args.name)) - DATA_ROUTE[#DATA_ROUTE + 1] = { - blip = blip, - coords = data.coords, - time = Config.Navigation.timeout - } + DATA_BLIP[#DATA_BLIP + 1] = { + blip = blip, + coords = data.coords, + time = Config.Navigation.timeout + } - Config.Notification(nil, (Strings.navi_set):format(data.label)) - end + RenderRoute(data.coords) + CORE.Bridge.notification((Strings.navi_set2):format(data.name)) end, } end lib.registerContext({ - id = 'zrx_personalmenu:personal_menu:navigation', - title = Strings.menu_navi, + id = 'zrx_personalmenu:personal_menu:navigation:preset', + title = Strings.menu_navi_preset, options = MENU, - menu = 'zrx_personalmenu:personal_menu:main' + menu = 'zrx_personalmenu:personal_menu:navigation' }) - lib.showContext('zrx_personalmenu:personal_menu:navigation') + lib.showContext('zrx_personalmenu:personal_menu:navigation:preset') +end + +RenderRoute = function(coords) + if #DATA_ROUTE.coords < 1 then + local pedCoords = GetEntityCoords(cache.ped) + + DATA_ROUTE.coords[#DATA_ROUTE.coords + 1] = vector3(pedCoords.x, pedCoords.y, pedCoords.z) + end + + if coords then + DATA_ROUTE.coords[#DATA_ROUTE.coords + 1] = vector3(coords.x, coords.y, coords.z) + DATA_ROUTE.last = vector3(coords.x, coords.y, coords.z) + end + + ClearGpsMultiRoute() + SetGpsMultiRouteRender(false) + StartGpsMultiRoute(26, false, true) + + print(CORE.Shared.DumpTable(DATA_ROUTE.coords)) + for i, data in ipairs(DATA_ROUTE.coords) do + print(i, data) + AddPointToGpsMultiRoute(data.x, data.y, data.z) + end + + SetGpsMultiRouteRender(true) +end + +RemoveDestionation = function(index) + print('rem', DATA_ROUTE.last, DATA_BLIP[index].coords) + if DATA_ROUTE.last == DATA_BLIP[index].coords then + print('rem1', #DATA_ROUTE.coords) + if #DATA_ROUTE.coords - 1 > 1 then + print('rem2', DATA_ROUTE.coords[2]) + DATA_ROUTE.last = DATA_ROUTE.coords[2] + else + print('rem3') + DATA_ROUTE.coords = {} + DATA_ROUTE.last = nil + ClearGpsMultiRoute() + end + end + + for i, data in pairs(DATA_ROUTE.coords) do + if DATA_BLIP[index].coords == data then + DATA_ROUTE.coords[i] = nil + DATA_ROUTE.coords = CORE.Shared.SortTableKeys(DATA_ROUTE.coords) + RenderRoute() + end + end + + SetBlipRoute(DATA_BLIP[index].blip, false) + RemoveBlip(DATA_BLIP[index].blip) + DATA_BLIP[index] = nil end OnBooster = function(state) @@ -1655,7 +2029,6 @@ OnBooster = function(state) CascadeShadowsSetCascadeBoundsScale(5.0) SetFlashLightFadeDistance(10.0) SetLightsCutoffDistanceTweak(10.0) - DistantCopCarSirens(true) SetArtificialLightsState(false) else RopeDrawShadowEnabled(false) @@ -1668,7 +2041,6 @@ OnBooster = function(state) CascadeShadowsSetCascadeBoundsScale(0.0) SetFlashLightFadeDistance(0.0) SetLightsCutoffDistanceTweak(0.0) - DistantCopCarSirens(false) end local pedCoords @@ -1707,11 +2079,6 @@ OnBooster = function(state) end) end -PayBill = function(bill) - ESX.TriggerServerCallback('esx_billing:payBill', function() - end, bill) -end - StartCooldown = function() if not Config.Cooldown then return end COOLDOWN = true @@ -1724,7 +2091,7 @@ StartCooldown = function() end IsGradeAllowed = function() - return not not Config.Company.allowedGrades[ESX.PlayerData.job.grade_name] + return not not Config.Company.allowedGrades[CORE.Bridge.getVariables().job.grade_name] end IsVehicleValid = function() diff --git a/configuration/config.lua b/configuration/config.lua index f55af35..97dfedd 100644 --- a/configuration/config.lua +++ b/configuration/config.lua @@ -36,10 +36,10 @@ Config.Navigation = { timeout = 120, --| In seconds checkDistance = 25, --| In GTA Units destinations = { --| Place here your locations - { label = 'Police Station', coords = vector3(407.6613, -986.1854, 29.2603-0.9), icon = 'fa-solid fa-building-shield' }, - { label = 'Medical Center', coords = vector3(291.7147, -586.7615, 43.1760-0.9), icon = 'fa-solid fa-house-medical' }, - { label = 'Mechanic', coords = vector3(-377.3869, -131.3874, 38.6804-0.9), icon = 'fa-solid fa-toolbox' }, - { label = 'Meetingpoint', coords = vector3(217.9133, -850.6498, 30.1731-0.9), icon = 'fa-solid fa-location-dot' }, + { name = 'Police Station', coords = vector3(407.6613, -986.1854, 29.2603-0.9), icon = 'fa-solid fa-building-shield' }, + { name = 'Medical Center', coords = vector3(291.7147, -586.7615, 43.1760-0.9), icon = 'fa-solid fa-house-medical' }, + { name = 'Mechanic', coords = vector3(-377.3869, -131.3874, 38.6804-0.9), icon = 'fa-solid fa-toolbox' }, + { name = 'Meetingpoint', coords = vector3(217.9133, -850.6498, 30.1731-0.9), icon = 'fa-solid fa-location-dot' }, }, route = function(coords, text) --| Change it if you know what you are doing local blip = AddBlipForCoord(coords.x, coords.y, coords.z) @@ -52,13 +52,18 @@ Config.Navigation = { BeginTextCommandSetBlipName('STRING') AddTextComponentSubstringPlayerName(text) EndTextCommandSetBlipName(blip) - SetBlipRoute(blip, true) - SetBlipRouteColour(blip, 26) return blip end } +--| Place here your pay bill actions +Config.PayBill = function(bill) + local ESX = exports.es_extended:getSharedObject() + ESX.TriggerServerCallback('esx_billing:payBill', function() + end, bill) +end + --| Place here your links/informations Config.Information = { { label = 'Discord', value = 'discord.gg/example' }, @@ -90,7 +95,7 @@ end --| Place here your account definition of ESX --| Change it if you know what you are doing -Config.ESXAccounts = { +Config.Accounts = { bank = 'bank', money = 'money', black_money = 'black_money' @@ -99,14 +104,21 @@ Config.ESXAccounts = { --| Player here your labels for each license --| Key is the type, the value is the label Config.Licenses = { - dmv = 'Theoretical driving test' + dmv = 'Theoretical driving test', + drive = 'Driver license', } --| Place here your punish actions Config.PunishPlayer = function(player, reason) if not IsDuplicityVersion() then return end - if Webhook.Settings.punish then - DiscordLog(player, 'PUNISH', reason, 'punish') + if Webhook.Links.punish:len() > 0 then + local message = ([[ + The player got punished + + Reason: **%s** + ]]):format(reason) + + CORE.Server.DiscordLog(player, 'Punish', message, Webhook.Links.punish) end DropPlayer(player, reason) @@ -114,7 +126,7 @@ end --| Place your checks here before the personal menu opens Config.CanOpenMenu = function() - return ESX.IsPlayerLoaded() + return CORE.Bridge.isPlayerLoaded() end --| Place here your checks before the vehicle menu opens @@ -122,15 +134,6 @@ Config.CanOpenExtras = function() return GetVehicleBodyHealth(GetVehiclePedIsIn(cache.ped, false)) >= 950 end ---| Place here your notification -Config.Notification = function(player, msg) - if IsDuplicityVersion() then - TriggerClientEvent('esx:showNotification', player, msg, 'info') - else - ESX.ShowNotification(msg) - end -end - --| Add here your give keys export Config.GiveVehicleKeys = function(player, vehicle) local plate = GetVehicleNumberPlateText(vehicle) @@ -151,14 +154,4 @@ Config.RemoveVehicleKeys = function(player, vehicle) else --| exports.wasabi_carlock:RemoveKeys(plate) end -end - ---| Place here your esx import ---| Change it if you know what you are doing -Config.EsxImport = function() - if IsDuplicityVersion() then - return exports.es_extended:getSharedObject() - else - return exports.es_extended:getSharedObject() - end end \ No newline at end of file diff --git a/configuration/strings.lua b/configuration/strings.lua index 83272de..311b917 100644 --- a/configuration/strings.lua +++ b/configuration/strings.lua @@ -6,24 +6,27 @@ Strings = { no_nearby = 'There is no player nearby', on_cooldown = 'Calm down!', male = 'Male', - female = 'female', + female = 'Female', + not_fill = 'Fullfill the dialog properly', menu_main = 'Personal menu', - menu_info = 'Personal menu - Info', - menu_info_lice = 'Personal menu - Informations licenses', + menu_info = 'Personal menu - Informations', + menu_info_lice = 'Personal menu - Licenses', menu_idcard = 'Personal menu - IDCard', menu_clothing = 'Personal menu - Clothing', menu_veh = 'Personal menu - Vehicle', - menu_veh_give = 'Personal menu - Give Vehicle', - menu_veh_extra = 'Personal menu - Vehicle extras', - menu_veh_livery = 'Personal menu - Vehicle liverys', - menu_veh_lights = 'Personal menu - Vehicle lights', - menu_veh_win = 'Personal menu - Vehicle windows', - menu_veh_doors = 'Personal menu - Vehicle doors', + menu_veh_extra = 'Personal menu - Extras', + menu_veh_livery = 'Personal menu - Liverys', + menu_veh_lights = 'Personal menu - Lights', + menu_veh_win = 'Personal menu - Windows', + menu_veh_doors = 'Personal menu - Doors', menu_setting = 'Personal menu - Settings', menu_company = 'Personal menu - Company', menu_bills = 'Personal menu - Bills', menu_navi = 'Personal menu - Navigation', + menu_navi_preset = 'Personal menu - Preset', + menu_navi_my = 'Personal menu - My', + menu_navi_setting = 'Personal menu - Settings', info_title = 'Informations', info_desc = 'Show your character informations', @@ -55,7 +58,7 @@ Strings = { player_title = 'Player', player_desc = 'ID: #%s - Name: %s - Ping: %s', --| arg1: Server ID - arg2: Player Name - arg3: Player ping - rpname_title = 'Name', + rpname_title = 'Firstname - Lastname', rpname_desc = '%s', --| arg1: RP Name dob_title = 'Date Of Birth', @@ -64,8 +67,8 @@ Strings = { height_title = 'Height', height_desc = '%s', --| arg1: RP Height - sex_title = 'Sex', - sex_desc = '%s', --| arg1: RP Sex + gender_title = 'Gender', + gender_desc = '%s', --| arg1: RP Gender job_title = 'Job', job_desc = 'Job: %s', --| arg1: Job name @@ -228,10 +231,10 @@ Strings = { trunk_title = 'Trunk', trunk_desc = 'Open/Close trunk', - all_doors_close_title = 'Open all doors', + all_doors_close_title = 'Open doors', all_doors_close_desc = 'Open all doors', - all_doors_open_title = 'Close all doors', + all_doors_open_title = 'Close doors', all_doors_open_desc = 'Close all doors', graphic_title = 'Graphicmod', @@ -274,13 +277,68 @@ Strings = { company_not_same = 'You are not in the same company', company_not_permitted = 'You are not permitted to do this', + navi_preset = 'Preset Navigation', + navi_preset_desc = 'Open the preset navigation', + + navi_my = 'My Navigation', + navi_my_desc = 'Open saved navigation', + + navi_create = 'Create navigation', + navi_create_desc = 'Create a new navigation', + navi_create_title = 'Create navigation', + navi_create_name = 'Name', + navi_create_name_desc = 'The navigation name', + navi_create_coords = 'Coords', + navi_create_coords_desc = 'The navigation coords', + navi_create_coords_default = '%s | %s | %s', --| arg1: X Coord - arg2: Y Coord - arg3: Z Coord + navi_create_street = 'Street', + navi_create_street_desc = 'The street name', + navi_create_street_default = '%s', --| arg1: Street name + + navi_manage = 'Click to manage', + + navi_setting = 'Information', + navi_setting_desc = 'Hover to see', + navi_setting_name = 'Name', + navi_setting_name_desc = '%s', + navi_setting_coords = 'Coords', + navi_setting_coords_desc = '%s | %s | %s', --| arg1: X Coord - arg2: Y Coord - arg3: Z Coord + navi_setting_street = 'Street', + navi_setting_street_desc = '%s', --| arg1: Street name + + navi_set = 'Waypoint', + navi_set_desc = 'Set the waypoint', + + navi_edit = 'Edit', + navi_edit_desc = 'Edit the navigation', + navi_edit_title = 'Edit navigation', + navi_edit_name = 'Name', + navi_edit_name_desc = 'The navigation name', + navi_edit_coords = 'Coords', + navi_edit_coords_desc = 'The navigation coords', + navi_edit_coords_default = '%s | %s | %s', --| arg1: X Coord - arg2: Y Coord - arg3: Z Coord + navi_edit_street = 'Street', + navi_edit_street_desc = 'The street name', + navi_edit_street_default = '%s', --| arg1: Street name + + navi_delete = 'Delete', + navi_delete_desc = 'Delete the navigation', + navi_desc2 = 'Press to set waypoint', navi_dest = 'Destination: %s', --| arg1: Destination name - navi_set = 'You set a waypoint to %s', --| arg1: Destination name + navi_set2 = 'You set a waypoint to %s', --| arg1: Destination name navi_timeout = 'Your waypoint got deleted due to timeout!', navi_reached = 'You reached your destination!', - navi_already = 'You already set a waypoint at %s', + navi_street = 'Street', + navi_street_desc = '%s', --| arg1: Street name + + navi_clear = 'Clear navigation', + navi_clear_desc = 'Clear all your current destinations', + navi_clear_notify = 'You cleared your navigation system', + + navi_no = 'No navigation', + navi_no_desc = 'You dont have any navigation', bill_title = 'Bill #%s', --| arg1: Bill ID bill_desc = 'Press to pay bill', diff --git a/configuration/webhook.lua b/configuration/webhook.lua index a599d7e..01fc5e9 100644 --- a/configuration/webhook.lua +++ b/configuration/webhook.lua @@ -4,30 +4,9 @@ Webhook = {} Webhook.Links = { punish = '', callback = '', - company = '' -} - ---| Enable certain logs? -Webhook.Settings = { - punish = true, - callback = true, - company = true -} - ---| Execlude certain information -Webhook.Execlude = { - name = false, - player = false, - ping = false, - discord = false, - fivem = false, - license = false, - license2 = false, - hwid = false, - steam = false, - xbl = false, - guid = false, - ip = false, - country = false, - vpn = false, + company = '', + giveVehicle = '', + createNav = '', + deleteNav = '', + editNav = '', } \ No newline at end of file diff --git a/fxmanifest.lua b/fxmanifest.lua index fbc5669..2924c00 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -5,10 +5,10 @@ use_experimental_fxv2_oal 'yes' author 'zRxnx' description 'Advanced personal menu system' -version '2.2.0' +version '2.3.0' dependencies { - 'es_extended', + 'zrx_utility', 'ox_lib', 'oxmysql' } @@ -17,7 +17,6 @@ shared_scripts { '@ox_lib/init.lua', 'configuration/config.lua', 'configuration/strings.lua', - 'shared/*.lua' } client_scripts { diff --git a/readme.md b/readme.md index 0f96710..948ab99 100644 --- a/readme.md +++ b/readme.md @@ -12,12 +12,14 @@ An advanced personal menu system for FiveM - Settings (FPS Booster, Graphicmod) - Bills (View, Pay) - Company (Society Infos, Hire/Fire/Promote/Derank Player) -- Navigator (Blips, multiple destinations) +- Navigator (presets, create/edit/delete own navigator, multiple routes) - Server Informations - Highly configurable - Protected Events - Discord log with many information - Update checker +- Optimized +- Synced - 0.0 ms on idle - 0.0 ms while in use @@ -29,7 +31,7 @@ An advanced personal menu system for FiveM ## Requirements -- es_extended (> 1.6.0) +- zrx_utility (latest) - ox_lib (latest) - oxmysql (latest) - esx_addonaccount (optional) @@ -37,9 +39,9 @@ An advanced personal menu system for FiveM ## Preview -### [Video](https://youtu.be/H1BCozrlTGY?si=L9mnTpGWp-GMA1YJ) +### [Video](https://youtu.be/j_GskkMD2X4?si=Me0IN8cFORnBqMQw) -![Discord Log](https://i.imgur.com/S8Hvzl3.png) +![Discord Log](https://i.imgur.com/TDfftS2.png) ![Main Page](https://i.imgur.com/LexEEA9.png) ## Installation diff --git a/server/functions.lua b/server/functions.lua index fa15761..99db8f8 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -1,103 +1,128 @@ -COOLDOWN = {} -local GetPlayerName = GetPlayerName -local GetNumPlayerTokens = GetNumPlayerTokens -local GetPlayerGuid = GetPlayerGuid -local GetPlayerIdentifierByType = GetPlayerIdentifierByType -local GetPlayerToken = GetPlayerToken -local PerformHttpRequest = PerformHttpRequest -local GetPlayerPing = GetPlayerPing -local GetResourceMetadata = GetResourceMetadata -local GetCurrentResourceName = GetCurrentResourceName - Player = { Hire = function(xPlayer, xTarget) - local xJob, yJob = xPlayer.getJob(), xTarget.getJob() + local xJob, yJob = xPlayer.job, xTarget.job if not IsGradeAllowed(xJob) then - Config.Notification(xPlayer.source, Strings.company_not_permitted) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_permitted) elseif xJob.name == yJob.name then - Config.Notification(xPlayer.source, (Strings.company_hire_already):format(xTarget.getName())) + CORE.Bridge.notification(xPlayer.source, (Strings.company_hire_already):format(xTarget.getName())) else - xTarget.setJob(xJob.name, 0) - - Config.Notification(xPlayer.source, (Strings.company_you_hired):format(xTarget.getName())) - Config.Notification(xTarget.source, (Strings.company_by_hired):format(xPlayer.getName())) - - if Webhook.Settings.company then - DiscordLog(xPlayer.source, 'HIRE', ('Player %s (%s) hired %s (%s) for (%s)'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source, PLAYER_CACHE[xTarget.source].name, xTarget.source, xJob.name:upper())) + CORE.Bridge.setJob(xTarget.player, xJob.name, 0) + + CORE.Bridge.notification(xPlayer.source, (Strings.company_you_hired):format(xTarget.getName())) + CORE.Bridge.notification(xTarget.source, (Strings.company_by_hired):format(xPlayer.getName())) + + if Webhook.Links.company:len() > 0 then + local message = ([[ + The player hired a person + + Server ID: **%s** + Playername: **%s** + Job: **%s** + Grade: **0** + ]]):format(xTarget.player, PLAYER_CACHE[xTarget.source].name, xJob.name) + + CORE.Server.DiscordLog(xPlayer.player, 'HIRE', message, Webhook.Links.company) end end end, Fire = function(xPlayer, xTarget) - local xJob, yJob = xPlayer.getJob(), xTarget.getJob() + local xJob, yJob = xPlayer.job, xTarget.job if not IsGradeAllowed(xJob) then - Config.Notification(xPlayer.source, Strings.company_not_permitted) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_permitted) elseif xJob.name ~= yJob.name then - Config.Notification(xPlayer.source, Strings.company_not_same) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_same) elseif xJob.name == yJob.name then - if ESX.DoesJobExist(Config.Company.default.job, Config.Company.default.grade) then - xTarget.setJob(Config.Company.default.job, Config.Company.default.grade) + if CORE.Bridge.doesJobExist(Config.Company.default.job, Config.Company.default.grade) then + CORE.Bridge.setJob(xTarget.player, Config.Company.default.job, Config.Company.default.grade) else - xTarget.setJob('unemployed', 0) + CORE.Bridge.setJob(xTarget.player, 'unemployed', 0) end - Config.Notification(xPlayer.source, (Strings.company_you_fired):format(xTarget.getName())) - Config.Notification(xTarget.source, (Strings.company_by_fired):format(xPlayer.getName())) + CORE.Bridge.notification(xPlayer.source, (Strings.company_you_fired):format(xTarget.getName())) + CORE.Bridge.notification(xTarget.source, (Strings.company_by_fired):format(xPlayer.getName())) - if Webhook.Settings.company then - DiscordLog(xPlayer.source, 'FIRE', ('Player %s (%s) fired %s (%s) from (%s)'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source, PLAYER_CACHE[xTarget.source].name, xTarget.source, xJob.name:upper()), 'company') + if Webhook.Links.company:len() > 0 then + local message = ([[ + The player fired a person + + Server ID: **%s** + Playername: **%s** + Job: **%s** + Grade: **%s** + ]]):format(xTarget.player, PLAYER_CACHE[xTarget.source].name, yJob.name, yJob.grade) + + CORE.Server.DiscordLog(xPlayer.player, 'FIRE', message, Webhook.Links.company) end end end, Promote = function(xPlayer, xTarget) - local xJob, yJob = xPlayer.getJob(), xTarget.getJob() + local xJob, yJob = xPlayer.job, xTarget.job if not IsGradeAllowed(xJob) then - Config.Notification(xPlayer.source, Strings.company_not_permitted) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_permitted) elseif xJob.name ~= yJob.name then - Config.Notification(xPlayer.source, Strings.company_not_same) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_same) elseif xJob.name == yJob.name then local grade = xJob.grade + 1 - if ESX.DoesJobExist(xJob.name, grade) then - xTarget.setJob(xJob.name, grade) + if CORE.Bridge.doesJobExist(xJob.name, grade) then + CORE.Bridge.setJob(xTarget.player, xJob.name, grade) + + CORE.Bridge.notification(xPlayer.source, (Strings.company_you_promote):format(xTarget.getName(), xJob.grade_label, grade)) + CORE.Bridge.notification(xTarget.source, (Strings.company_by_promote):format(xPlayer.getName(), xJob.grade_label, grade)) - Config.Notification(xPlayer.source, (Strings.company_you_promote):format(xTarget.getName(), xJob.grade_label, grade)) - Config.Notification(xTarget.source, (Strings.company_by_promote):format(xPlayer.getName(), xJob.grade_label, grade)) + if Webhook.Links.company:len() > 0 then + local message = ([[ + The player promoted a person + + Server ID: **%s** + Playername: **%s** + Job: **%s** + Grade: **%s** + ]]):format(xTarget.player, PLAYER_CACHE[xTarget.source].name, xJob.name, grade) - if Webhook.Settings.company then - DiscordLog(xPlayer.source, 'PROMOTE', ('Player %s (%s) promoted %s (%s) to (%s - %s)'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source, PLAYER_CACHE[xTarget.source].name, xTarget.source, xJob.name:upper(), grade), 'company') + CORE.Server.DiscordLog(xPlayer.player, 'PROMOTE', message, Webhook.Links.company) end else - Config.Notification(xPlayer.source, Strings.company_higher) + CORE.Bridge.notification(xPlayer.source, Strings.company_higher) end end end, Derank = function(xPlayer, xTarget) - local xJob, yJob = xPlayer.getJob(), xTarget.getJob() + local xJob, yJob = xPlayer.job, xTarget.job if not IsGradeAllowed(xJob) then - Config.Notification(xPlayer.source, Strings.company_not_permitted) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_permitted) elseif xJob.name ~= yJob.name then - Config.Notification(xPlayer.source, Strings.company_not_same) + CORE.Bridge.notification(xPlayer.source, Strings.company_not_same) elseif xJob.name == yJob.name then local grade = xJob.grade - 1 - if ESX.DoesJobExist(xJob.name, grade) then - xTarget.setJob(xJob.name, grade) + if CORE.Bridge.doesJobExist(xJob.name, grade) then + CORE.Bridge.setJob(xTarget.player, xJob.name, grade) + + CORE.Bridge.notification(xPlayer.source, (Strings.company_you_derank):format(xTarget.getName(), xJob.grade_label, grade)) + CORE.Bridge.notification(xTarget.source, (Strings.company_by_derank):format(xPlayer.getName(), xJob.grade_label, grade)) - Config.Notification(xPlayer.source, (Strings.company_you_derank):format(xTarget.getName(), xJob.grade_label, grade)) - Config.Notification(xTarget.source, (Strings.company_by_derank):format(xPlayer.getName(), xJob.grade_label, grade)) + if Webhook.Links.company:len() > 0 then + local message = ([[ + The player deranked a person + + Server ID: **%s** + Playername: **%s** + Job: **%s** + Grade: **%s** + ]]):format(xTarget.player, PLAYER_CACHE[xTarget.source].name, xJob.name, grade) - if Webhook.Settings.company then - DiscordLog(xPlayer.source, 'DERANK', ('Player %s (%s) deranked %s (%s) to (%s - %s)'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source, PLAYER_CACHE[xTarget.source].name, xTarget.source, xJob.name:upper(), grade), 'company') + CORE.Server.DiscordLog(xPlayer.player, 'DERANK', message, Webhook.Links.company) end else - Config.Notification(xPlayer.source, Strings.company_lower) + CORE.Bridge.notification(xPlayer.source, Strings.company_lower) end end end, @@ -120,143 +145,6 @@ Player = { end } -GetPlayerData = function(player) - local p1, p2 = promise.new(), promise.new() - local name = GetPlayerName(player) - local numTokens = GetNumPlayerTokens(player) - local guid = GetPlayerGuid(player) - local fivem = (GetPlayerIdentifierByType(player, 'fivem') or 'NOT FOUND'):gsub('fivem:', '') - local steam = (GetPlayerIdentifierByType(player, 'steam') or 'NOT FOUND'):gsub('steam:', '') - local license = (GetPlayerIdentifierByType(player, 'license') or 'NOT FOUND'):gsub('license:', '') - local license2 = (GetPlayerIdentifierByType(player, 'license2'):gsub('license2:', '') or 'NOT FOUND'):gsub('license2:', '') - local discord = (GetPlayerIdentifierByType(player, 'discord') or 'NOT FOUND'):gsub('discord:', '') - local xbl = (GetPlayerIdentifierByType(player, 'xbl') or 'NOT FOUND'):gsub('xbl:', '') - local liveid = (GetPlayerIdentifierByType(player, 'liveid') or 'NOT FOUND'):gsub('liveid:', '') - local ip = (GetPlayerIdentifierByType(player, 'ip'):gsub('ip:', '') or 'NOT FOUND'):gsub('ip:', '') - local country = 'NOT FOUND' - local vpn = false - local hwids = {} - - for i = 0, numTokens, 1 do - hwids[#hwids + 1] = GetPlayerToken(player, i) - end - - PerformHttpRequest(('http://ip-api.com/json/%s?fields=61439'):format(ip), function(_, result, _) - if result then - local data = json.decode(result) - - p1:resolve(data.country or 'NOT FOUND') - p2:resolve(not not (data.hosting or data.proxy)) - end - end) - - country = Citizen.Await(p1) - vpn = Citizen.Await(p2) - - return { - player = player, - name = name, - guid = guid, - hwids = hwids, - steam = steam, - license = license, - license2 = license2, - fivem = fivem, - xbl = xbl, - ip = ip, - discord = discord, - liveid = liveid, - country = country, - vpn = vpn - } -end - -DiscordLog = function(player, title, message, webhook) - if Webhook.Links[webhook]:len() <= 0 then return end - local description = ('%s\n\n'):format(message) - - if not Webhook.Execlude.name then - description = ('%s `👤` **Player**: %s\n'):format(description, PLAYER_CACHE[player].name) - end - - if not Webhook.Execlude.player then - description = ('%s `#ī¸âƒŖ` **Server ID**: `%s`\n'):format(description, PLAYER_CACHE[player].player) - end - - if not Webhook.Execlude.ping then - description = ('%s `đŸ“ļ` **Player Ping**: `%sms`\n'):format(description, GetPlayerPing(player) ) - end - - if not Webhook.Execlude.discord then - description = ('%s `📌` **Discord ID**: `%s` <@%s>\n'):format(description, PLAYER_CACHE[player].discord, PLAYER_CACHE[player].discord) - end - - if not Webhook.Execlude.fivem then - description = ('%s `🟧` **FiveM ID**: `%s`\n'):format(description, PLAYER_CACHE[player].fivem) - end - - if not Webhook.Execlude.license then - description = ('%s `📀` **License ID**: `%s`\n'):format(description, PLAYER_CACHE[player].license) - end - - if not Webhook.Execlude.license2 then - description = ('%s `đŸ’ŋ` **License2 ID**: `%s`\n'):format(description, PLAYER_CACHE[player].license2) - end - - if not Webhook.Execlude.hwid then - description = ('%s `đŸ’ģ` **Hardware ID**: `%s`\n'):format(description, PLAYER_CACHE[player].hwids[1]) - end - - if not Webhook.Execlude.steam then - description = ('%s `👾` **Steam ID**: `%s`\n'):format(description, PLAYER_CACHE[player].steam) - end - - if not Webhook.Execlude.xbl then - description = ('%s `🕹ī¸` **XBOX Live ID**: `%s`\n'):format(description, PLAYER_CACHE[player].xbl) - end - - if not Webhook.Execlude.guid then - description = ('%s `⚙ī¸` **GUID**: `%s`\n'):format(description, PLAYER_CACHE[player].guid) - end - - if not Webhook.Execlude.ip then - description = ('%s `🌐` **IP**: ||%s||\n'):format(description, PLAYER_CACHE[player].ip) - end - - if not Webhook.Execlude.country then - description = ('%s `🌍` **Country**: ||%s||\n'):format(description, PLAYER_CACHE[player].country) - end - - if not Webhook.Execlude.vpn then - description = ('%s `🤖` **VPN**: ||%s||\n'):format(description, PLAYER_CACHE[player].vpn) - end - - local embed = { - { - ['color'] = 255, - ['title'] = title, - ['description'] = description, - ['footer'] = { - ['text'] = ('Made by %s | %s'):format(GetResourceMetadata(GetCurrentResourceName(), 'author'), os.date()), - ['icon_url'] = 'https://i.imgur.com/QOjklyr.png' - }, - - ['author'] = { - ['name'] = 'zrx_personalmenu', - ['icon_url'] = 'https://i.imgur.com/QOjklyr.png' - } - } - } - - PerformHttpRequest(Webhook.Links[webhook], nil, 'POST', json.encode({ - username = 'ZRX LOGS', - embeds = embed, - avatar_url = 'https://i.imgur.com/QOjklyr.png' - }), { - ['Content-Type'] = 'application/json' - }) -end - IsGradeAllowed = function(job) return not not Config.Company.allowedGrades[job.grade_name] end \ No newline at end of file diff --git a/server/server.lua b/server/server.lua index aefd63c..51ae507 100644 --- a/server/server.lua +++ b/server/server.lua @@ -1,111 +1,129 @@ -ESX, PLAYER_CACHE = Config.EsxImport(), {} +CORE = exports.zrx_utility:GetUtility() +COOLDOWN, PLAYER_CACHE = {}, {} local GetPlayers = GetPlayers local GetPlayerPing = GetPlayerPing -RegisterNetEvent('esx:playerLoaded', function(player, xPlayer, isNew) - PLAYER_CACHE[player] = GetPlayerData(player) +RegisterNetEvent('zrx_utility:bridge:playerLoaded', function(player, xPlayer, isNew) + PLAYER_CACHE[player] = CORE.Server.GetPlayerCache(player) end) CreateThread(function() - for i, data in pairs(GetPlayers()) do - data = tonumber(data) - PLAYER_CACHE[data] = GetPlayerData(data) + if Config.CheckForUpdates then + CORE.Server.CheckVersion('zrx_personalmenu') + end + + MySQL.Sync.execute([[ + CREATE Table IF NOT EXISTS `zrx_personalmenu` ( + `identifier` varchar(255) DEFAULT NULL, + `data` longtext DEFAULT NULL, + PRIMARY KEY (`identifier`) + ) ENGINE=InnoDB; + ]]) + + for i, player in pairs(GetPlayers()) do + player = tonumber(player) + PLAYER_CACHE[player] = CORE.Server.GetPlayerCache(player) end end) -lib.callback.register('zrx_personalmenu:server:getPlayerData', function(source) - if Player.HasCooldown(source) then return {} end - local xPlayer = ESX.GetPlayerFromId(source) +lib.callback.register('zrx_personalmenu:server:getPlayerData', function(player) + if Player.HasCooldown(player) then return {} end + local xPlayer = CORE.Bridge.getVariables(player) + + if Webhook.Links.callback:len() > 0 then + local message = [[ + The player requested their data + ]] - if Webhook.Settings.callback then - DiscordLog(xPlayer.source, 'PLAYERDATA', ('Player %s (%s) requested their data'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source), 'callback') + CORE.Server.DiscordLog(xPlayer.player, 'PLAYERDATA', message, Webhook.Links.callback) end return { - job = xPlayer.getJob(), - name = xPlayer.getName(), - dob = xPlayer.variables.dateofbirth, - height = xPlayer.variables.height, - bank = xPlayer.getAccount(Config.ESXAccounts.bank).money, - money = xPlayer.getAccount(Config.ESXAccounts.money).money, - black_money = xPlayer.getAccount(Config.ESXAccounts.black_money).money, - ping = GetPlayerPing(xPlayer.source) + job = xPlayer.job, + name = xPlayer.name, + dob = xPlayer.dob, + height = xPlayer.height, + bank = CORE.Bridge.getAccount(xPlayer.player, Config.Accounts.bank).money, + money = CORE.Bridge.getAccount(xPlayer.player, Config.Accounts.money).money, + black_money = CORE.Bridge.getAccount(xPlayer.player, Config.Accounts.black_money).money, + ping = GetPlayerPing(xPlayer.player) } end) -lib.callback.register('zrx_personalmenu:server:getPlayerBills', function(source) - if Player.HasCooldown(source) then return {} end - local xPlayer = ESX.GetPlayerFromId(source) - local results = MySQL.query.await('SELECT * FROM `billing` WHERE `identifier` = ?', { xPlayer.identifier }) - local BILLS = {} +lib.callback.register('zrx_personalmenu:server:getPlayerBills', function(player) + if Player.HasCooldown(player) then return {} end + if Webhook.Links.callback:len() > 0 then + local message = [[ + The player requested their bills + ]] - if Webhook.Settings.callback then - DiscordLog(xPlayer.source, 'PLAYERBILLS', ('Player %s (%s) requested their bills'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source), 'callback') - end - - for k, data in pairs(results) do - BILLS[#BILLS + 1] = { - id = data.id, - label = data.label, - amount = data.amount - } + CORE.Server.DiscordLog(player, 'BILLS', message, Webhook.Links.callback) end - return BILLS + return CORE.Bridge.getBills(player) end) -lib.callback.register('zrx_personalmenu:server:getPlayerLicenses', function(source) - if Player.HasCooldown(source) then return {} end - local xPlayer = ESX.GetPlayerFromId(source) - local results = MySQL.query.await('SELECT * FROM `user_licenses` WHERE `owner` = ?', { xPlayer.identifier }) +lib.callback.register('zrx_personalmenu:server:getPlayerLicenses', function(player) + if Player.HasCooldown(player) then return {} end + if Webhook.Links.callback:len() > 0 then + local message = [[ + The player requested their licenses + ]] - if Webhook.Settings.callback then - DiscordLog(xPlayer.source, 'LICENSES', ('Player %s (%s) requested their licenses'):format(PLAYER_CACHE[xPlayer.source].name, xPlayer.source), 'callback') + CORE.Server.DiscordLog(player, 'LICENSES', message, Webhook.Links.callback) end - return results + return CORE.Bridge.getLicenses(player) end) -lib.callback.register('zrx_personalmenu:server:getSocietyData', function(source, job) - if Player.HasCooldown(source) then return {} end - local money = promise.new() +lib.callback.register('zrx_personalmenu:server:getSocietyData', function(player, job) + if Player.HasCooldown(player) then return {} end + if Webhook.Links.callback:len() > 0 then + local message = ([[ + The player requested society data - if Webhook.Settings.callback then - DiscordLog(source, 'SOCIETY', ('Player %s (%s) requested %s society data'):format(PLAYER_CACHE[source].name, source, job:upper()), 'callback') - end + Society: **%s** + ]]):format(job) - TriggerEvent('esx_addonaccount:getSharedAccount', ('society_%s'):format(job), function(account) - money:resolve(account.money) - end) + CORE.Server.DiscordLog(player, 'SOCIETY', message, Webhook.Links.callback) + end return { - money = Citizen.Await(money) + money = CORE.Bridge.getSocietyMoney(job) } end) -RegisterNetEvent('zrx_personalmenu:server:managePlayer', function(target, action) - action = action:lower() - if action ~= 'hire' and action ~= 'fire' and action ~= 'promote' and action ~= 'derank' then - if Webhook.Settings.punish then - DiscordLog(source, 'MANAGEPLAYER', ('Player %s (%s) tried to trigger "zrx_personalmenu:server:managePlayer"'):format(PLAYER_CACHE[source].name, source), 'punish') - end +lib.callback.register('zrx_personalmenu:server:getPlayerNavigation', function(player) + if Player.HasCooldown(player) then return {} end + local response = MySQL.query.await('SELECT `data` FROM `zrx_personalmenu` WHERE `identifier` = ?', { + PLAYER_CACHE[player].license + }) - return Config.PunishPlayer(source, 'Tried to trigger "zrx_personalmenu:server:managePlayer"') - elseif type(target) ~= 'number' then - if Webhook.Settings.punish then - DiscordLog(source, 'MANAGEPLAYER', ('Player %s (%s) tried to trigger "zrx_personalmenu:server:managePlayer"'):format(PLAYER_CACHE[source].name, source), 'punish') - end + if response[1] then + local OUTPUT = {} + local data = json.decode(response[1].data) - return Config.PunishPlayer(source, 'Tried to trigger "zrx_personalmenu:server:managePlayer"') - elseif source == target then - if Webhook.Settings.punish then - DiscordLog(source, 'MANAGEPLAYER', ('Player %s (%s) tried to trigger "zrx_personalmenu:server:managePlayer"'):format(PLAYER_CACHE[source].name, source), 'punish') + for k, data2 in pairs(data) do + OUTPUT[#OUTPUT + 1] = { + name = data2.name, + coords = vector3(data2.coords.x, data2.coords.y, data2.coords.z), + street = data2.street + } end + return OUTPUT + else + return {} + end +end) + +RegisterNetEvent('zrx_personalmenu:server:managePlayer', function(target, action) + action = action:lower() + if action ~= 'hire' and action ~= 'fire' and action ~= 'promote' and action ~= 'derank' or type(target) ~= 'number' or source == target then return Config.PunishPlayer(source, 'Tried to trigger "zrx_personalmenu:server:managePlayer"') end - local xPlayer, xTarget = ESX.GetPlayerFromId(source), ESX.GetPlayerFromId(target) + local xPlayer, xTarget = CORE.Bridge.getVariable(source), CORE.Bridge.getVariable(target) if action == 'hire' then Player.Hire(xPlayer, xTarget) @@ -118,23 +136,154 @@ RegisterNetEvent('zrx_personalmenu:server:managePlayer', function(target, action end end) +RegisterNetEvent('zrx_personalmenu:server:manageNavigation', function(action, name, coords, street, old) + local player = source + if action ~= 'edit' and action ~= 'create' and action ~= 'delete' or + type(action) ~= 'string' and type(coords) ~= 'string' and type(street) ~= 'string' then + return Config.PunishPlayer(player, 'Tried to trigger "zrx_personalmenu:server:manageNavigation"') + end + + local DATA = {} + local response = MySQL.query.await('SELECT `data` FROM `zrx_personalmenu` WHERE `identifier` = ?', { + PLAYER_CACHE[player].license + }) + + if action == 'edit' or action == 'create' then + DATA[#DATA + 1] = { + name = name[1], + coords = coords, + street = street, + } + + if action == 'create' then + if Webhook.Links.createNav:len() > 0 then + local message = ([[ + The player created a navigation + + Name: **%s** + Coords: **%s** + Street: **%s** + ]]):format(name, coords, street) + + CORE.Server.DiscordLog(player, 'CREATE NAVIGATION', message, Webhook.Links.createNav) + end + + CORE.Bridge.notification(player, 'You made a new navigation point') + elseif action == 'edit' then + if Webhook.Links.editNav:len() > 0 then + local message = ([[ + The player edited a navigation + + New + Name: **%s** + Coords: **%s** + Street: **%s** + + Old + Name: **%s** + Coords: **%s** + Street: **%s** + ]]):format(name, coords, street, old.name, old.coords, old.street) + + CORE.Server.DiscordLog(player, 'EDIT NAVIGATION', message, Webhook.Links.editNav) + end + + CORE.Bridge.notification(player, 'You edited a navigation point') + end + + if response[1] then + local data = json.decode(response[1].data) + + if old then + for k, data2 in pairs(data) do + if data2.name ~= old.name and data2.coords ~= old.coords and data2.street ~= old.street then + DATA[#DATA + 1] = { + name = data2.name, + coords = data2.coords, + street = data2.street + } + end + end + else + for k, data2 in pairs(data) do + DATA[#DATA + 1] = { + name = data2.name, + coords = data2.coords, + street = data2.street + } + end + end + + MySQL.update.await('UPDATE zrx_personalmenu SET data = ? WHERE identifier = ?', { + json.encode(DATA), PLAYER_CACHE[player].license + }) + else + MySQL.insert.await('INSERT INTO `zrx_personalmenu` (identifier, data) VALUES (?, ?)', { + PLAYER_CACHE[player].license, json.encode(DATA) + }) + end + elseif action == 'delete' then + local data = json.decode(response[1].data) + + for k, data2 in pairs(data) do + if data2.name ~= name and data2.coords ~= coords and data2.street ~= street then + DATA[#DATA + 1] = { + name = data2.name, + coords = data2.coords, + street = data2.street + } + end + + MySQL.update.await('UPDATE zrx_personalmenu SET data = ? WHERE identifier = ?', { + json.encode(DATA), PLAYER_CACHE[player].license + }) + end + + if Webhook.Links.deleteNav:len() > 0 then + local message = ([[ + The player deleted a navigation + + Name: **%s** + Coords: **%s** + Street: **%s** + ]]):format(name, coords, street) + + CORE.Server.DiscordLog(player, 'DELETE NAVIGATION', message, Webhook.Links.deleteNav) + end + + CORE.Bridge.notification(player, 'You delete the navigation point') + end +end) + RegisterNetEvent('zrx_personalmenu:server:giveCar', function(target, plate) if not target or not plate then return end - local xPlayer = ESX.GetPlayerFromId(source) + local xPlayer = CORE.Bridge.getVariable(source) local row = MySQL.single.await('SELECT `plate`, `owner` FROM `owned_vehicles` WHERE `plate` = ? LIMIT 1', { plate }) if row?.plate == plate and row?.owner == xPlayer.identifier then - local xTarget = ESX.GetPlayerFromId(target) + local xTarget = CORE.Bridge.getVariable(target) MySQL.update.await('UPDATE `owned_vehicles` SET `owner` = ? WHERE `plate` = ?', { xTarget.identifier, plate }) - Config.RemoveVehicleKeys(xPlayer.source, plate) - Config.GiveVehicleKeys(xTarget.source, plate) + Config.RemoveVehicleKeys(xPlayer.player, plate) + Config.GiveVehicleKeys(xTarget.player, plate) - Config.Notification(xTarget.source, (Strings.give_got):format(xPlayer.getName(), plate)) - Config.Notification(xPlayer.source, (Strings.give_given):format(plate, xTarget.getName())) + CORE.Bridge.notification(xTarget.player, (Strings.give_got):format(xPlayer.name, plate)) + CORE.Bridge.notification(xPlayer.player, (Strings.give_given):format(plate, xTarget.name)) + + if Webhook.Links.giveVehicle:len() > 0 then + local message = ([[ + The player gave a vehicle to a person + + Player ID: **%s** + Player Name: **%s** + Vehicle Plate: **%s** + ]]):format(xTarget.player, PLAYER_CACHE[xTarget.player].name, plate) + + CORE.Server.DiscordLog(xPlayer.player, 'GIVE VEHICLE', message, Webhook.Links.giveVehicle) + end else - Config.Notification(xPlayer.source, (Strings.give_cannot):format(plate)) + CORE.Bridge.notification(xPlayer.player, (Strings.give_cannot):format(plate)) end end)