From 1ef5bc04f69dd5b023734bc66209ad494e46ab23 Mon Sep 17 00:00:00 2001 From: swkeep Date: Thu, 29 Aug 2024 09:27:17 +0200 Subject: [PATCH] feat: attempt to make a qb-target compatibility layer bridge: framework related functions providers: where we replace exports --- interactionMenu/fxmanifest.lua | 12 +- interactionMenu/lua/bridge/main.lua | 52 +++ interactionMenu/lua/bridge/qb.lua | 90 +++++ interactionMenu/lua/client/menuContainer.lua | 93 ++++- interactionMenu/lua/frameworks/qb/client.lua | 109 ------ interactionMenu/lua/providers/qb-target.lua | 312 +++++++++++++++++ .../lua/providers/qb-target_test.lua | 324 ++++++++++++++++++ 7 files changed, 861 insertions(+), 131 deletions(-) create mode 100644 interactionMenu/lua/bridge/main.lua create mode 100644 interactionMenu/lua/bridge/qb.lua delete mode 100644 interactionMenu/lua/frameworks/qb/client.lua create mode 100644 interactionMenu/lua/providers/qb-target.lua create mode 100644 interactionMenu/lua/providers/qb-target_test.lua diff --git a/interactionMenu/fxmanifest.lua b/interactionMenu/fxmanifest.lua index aa1757b..f4e6e68 100644 --- a/interactionMenu/fxmanifest.lua +++ b/interactionMenu/fxmanifest.lua @@ -29,6 +29,10 @@ client_script { '@PolyZone/BoxZone.lua', '@PolyZone/CircleZone.lua', '@PolyZone/ComboZone.lua', + + -- bridges + 'lua/bridge/main.lua', + -- core 'lua/client/util.lua', 'lua/client/3dDuiMaker.lua', @@ -39,9 +43,8 @@ client_script { 'lua/client/garbageCollector.lua', -- providers - -- 'lua/providers/qb-target.lua', - -- 'lua/providers/qb-target_test.lua', - -- 'lua/providers/qb-target_debug.lua', + 'lua/providers/qb-target.lua', + 'lua/providers/qb-target_test.lua', -- examples / tests 'lua/examples/*.lua', @@ -53,8 +56,9 @@ server_script { files { 'lua/client/icons/*.*', + 'lua/bridge/qb.lua', } --- provide 'qb-target' +provide 'qb-target' lua54 'yes' diff --git a/interactionMenu/lua/bridge/main.lua b/interactionMenu/lua/bridge/main.lua new file mode 100644 index 0000000..d80e001 --- /dev/null +++ b/interactionMenu/lua/bridge/main.lua @@ -0,0 +1,52 @@ +local resource_name = GetCurrentResourceName() + +local LoadResourceFile = LoadResourceFile +local context = IsDuplicityVersion() and 'server' or 'client' + +string.split = function(str, pattern) + pattern = pattern or "[^%s]+" + if pattern:len() == 0 then + pattern = "[^%s]+" + end + local parts = { __index = table.insert } + setmetatable(parts, parts) + str:gsub(pattern, parts) + setmetatable(parts, nil) + parts.__index = nil + return parts +end + +local function loadModule(module_name, dir) + local chunk = LoadResourceFile(resource_name, ('%s.lua'):format(dir)) + + if chunk then + local fn, err = load(chunk) + if not fn or err then + return error(('\n^1Error (%s): %s^0'):format(dir, err), 3) + end + + local result = fn() + + return result[context]() + end +end + +---comment +---@param module string +local function link(module) + local sub = module:split('[^.]+') + local dir = ('lua/bridge/%s'):format(sub[1]) + return loadModule(sub[1], dir) +end + +Bridge = { + active = false +} + +if GetResourceState('qb-core') == 'started' then + local bridge_link = link('qb') + Bridge.hasItem = bridge_link.hasItem + Bridge.getJob = bridge_link.getJob + Bridge.getGang = bridge_link.getGang + Bridge.active = true +end diff --git a/interactionMenu/lua/bridge/qb.lua b/interactionMenu/lua/bridge/qb.lua new file mode 100644 index 0000000..b5e34aa --- /dev/null +++ b/interactionMenu/lua/bridge/qb.lua @@ -0,0 +1,90 @@ +local function updatePlayerItems(playerData, qb_items) + for _, itemData in pairs(playerData.items or {}) do + if qb_items[itemData.name] then + qb_items[itemData.name] = qb_items[itemData.name] + itemData.amount + else + qb_items[itemData.name] = itemData.amount + end + end +end + +local function reset(t) table.wipe(t) end + +return { + client = function() + local QBCore = exports['qb-core']:GetCoreObject() + local playerData = QBCore.Functions.GetPlayerData() or {} + local qb_items = {} + + AddEventHandler('QBCore:Client:OnPlayerLoaded', function() + playerData = QBCore.Functions.GetPlayerData() + reset(qb_items) + updatePlayerItems(playerData, qb_items) + end) + + RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() + playerData = {} + reset(qb_items) + end) + + RegisterNetEvent('QBCore:Player:SetPlayerData', function(val) + playerData = QBCore.Functions.GetPlayerData() + reset(qb_items) + updatePlayerItems(playerData, qb_items) + end) + + RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) + playerData = QBCore.Functions.GetPlayerData() + reset(qb_items) + updatePlayerItems(playerData, qb_items) + end) + + RegisterNetEvent('QBCore:Client:OnGangUpdate', function(GangInfo) + playerData = QBCore.Functions.GetPlayerData() + reset(qb_items) + updatePlayerItems(playerData, qb_items) + end) + + local function init() + playerData = QBCore.Functions.GetPlayerData() + reset(qb_items) + updatePlayerItems(playerData, qb_items) + end + + init() + + return { + ['getJob'] = function() + local job = playerData['job'] + return job.name, job.grade.level + end, + ['getGang'] = function() + local gang = playerData['gang'] + return gang.name, gang.grade.level + end, + ['hasItem'] = function(itemName, requiredAmount) + if not requiredAmount then requiredAmount = 1 end + local hasItem = qb_items[itemName] ~= nil + local hasEnough = hasItem and qb_items[itemName] >= requiredAmount or false + return hasItem, hasEnough + end, + ['hasItems'] = function(itemNames, requiredAmount) + if not requiredAmount then requiredAmount = 1 end + + for _, itemName in pairs(itemNames) do + local hasItem = qb_items[itemName] ~= nil + local hasEnough = hasItem and qb_items[itemName] >= requiredAmount or false + + if not hasEnough then + return false, itemName + end + end + + return true + end + } + end, + server = function() + + end +} diff --git a/interactionMenu/lua/client/menuContainer.lua b/interactionMenu/lua/client/menuContainer.lua index 6b82273..62f7228 100644 --- a/interactionMenu/lua/client/menuContainer.lua +++ b/interactionMenu/lua/client/menuContainer.lua @@ -166,6 +166,26 @@ end ---@field event? string "Event name to trigger when the option is selected" ---@field hide boolean "Indicates whether the option should be hidden" +local function transformRestrictions(t) + if not Bridge.active then return end + if not t then return end + if type(t) == 'string' then t = { t } end + local transformed = {} + + for index, name in pairs(t) do + if type(name) == 'string' then + transformed[name] = {} + elseif type(name) == 'table' then + transformed[index] = {} + for key, grade in pairs(name) do + transformed[index][grade] = true + end + end + end + + return transformed +end + local function buildOption(data, instance) for i, option in ipairs(data.options or {}) do local formatted = { @@ -177,6 +197,9 @@ local function buildOption(data, instance) style = option.style, progress = option.progress, icon = option.icon, + item = option.item, + job = option.job and transformRestrictions(option.job), + gang = option.gang and transformRestrictions(option.gang), flags = { dynamic = option.dynamic, @@ -234,19 +257,6 @@ local function classifyMenuInstance(instance) end end -local function transformJobData(data) - if not (data.extra and data.extra.job) then return end - - for job_name, raw_grades in pairs(data.extra.job) do - local job_grades = {} - - for _, grade in pairs(raw_grades) do - job_grades[grade] = true - end - data.extra.job[job_name] = job_grades - end -end - function Container.create(t) local invokingResource = GetInvokingResource() or 'interactionMenu' local id = t.id or Util.createUniqueId(Container.data) @@ -377,8 +387,6 @@ function Container.create(t) end buildOption(t, instance) - transformJobData(instance) - buildInteraction(t, instance.interactions, "onTrigger") buildInteraction(t, instance.interactions, "onSeen") buildInteraction(t, instance.interactions, "onExit") @@ -424,8 +432,6 @@ function Container.createGlobal(t) } buildOption(t, instance) - transformJobData(instance) - buildInteraction(t, instance.interactions, "onTrigger") buildInteraction(t, instance.interactions, "onSeen") buildInteraction(t, instance.interactions, "onExit") @@ -916,6 +922,11 @@ local function processData(params) zone = menuData.zone, distance = menuData.distance } + if interaction.payload then + for key, value in pairs(interaction.payload) do + data[key] = value + end + end try_unpack = false end else @@ -1076,6 +1087,49 @@ local function updateOptionVisibility(updatedElements, menuId, option, optionInd return false end +local function check_restrictions(restrictions) + if not Bridge.active then return true end + + local job, job_level = Bridge.getJob() + local gang, gang_level = Bridge.getGang() + + if restrictions.job then + local allowed_job_levels = restrictions.job[job] + if allowed_job_levels and (next(allowed_job_levels) == nil or allowed_job_levels[job_level]) then + return true, 'job' + end + end + + if restrictions.gang then + local allowed_gang_levels = restrictions.gang[gang] + if allowed_gang_levels and (next(allowed_gang_levels) == nil or allowed_gang_levels[gang_level]) then + return true, 'gang' + end + end + + return false +end + +local function frameworkOptionVisibilityRestrictions(updatedElements, menuId, option, optionIndex, menuOriginalData, pt) + if not Bridge.active then return false end + + local shouldHide = false + if option.item then + local res = Bridge.hasItem(option.item) + shouldHide = type(res) == "boolean" and not res + elseif option.job then + local res = check_restrictions({ + job = option.job, + gang = option.gang + }) + shouldHide = type(res) == "boolean" and not res + end + + option.flags.hide = shouldHide + + return false +end + --- calculate canInteract and update values and refresh UI ---@param scaleform table ---@param menuData table @@ -1104,13 +1158,16 @@ function Container.syncData(scaleform, menuData, refreshUI) for optionIndex, option in ipairs(menu.options) do local already_inserted = false - + -- #TODO: I think we should pass already_inserted to next step and check so we don't override it! already_inserted = evaluateDynamicValue(updatedElements, menuId, option, optionIndex, menuOriginalData, passThrough) already_inserted = evaluateBindValue(updatedElements, menuId, option, optionIndex, menuOriginalData, passThrough) already_inserted = updateOptionVisibility(updatedElements, menuId, option, optionIndex, menuOriginalData, passThrough) + already_inserted = frameworkOptionVisibilityRestrictions(updatedElements, menuId, option, optionIndex, + menuOriginalData, + passThrough) -- to hide option if its canInteract value has been changed if not already_inserted and option.flags.hide ~= nil and option.flags.hide ~= option.flags.previous_hide then diff --git a/interactionMenu/lua/frameworks/qb/client.lua b/interactionMenu/lua/frameworks/qb/client.lua deleted file mode 100644 index b533844..0000000 --- a/interactionMenu/lua/frameworks/qb/client.lua +++ /dev/null @@ -1,109 +0,0 @@ --- placeholder - -Bridge = {} -local QBCore = exports['qb-core']:GetCoreObject() - -local playerData = QBCore.Functions.GetPlayerData() or {} - -AddEventHandler('QBCore:Client:OnPlayerLoaded', function() - playerData = QBCore.Functions.GetPlayerData() -end) - -RegisterNetEvent('QBCore:Player:SetPlayerData', function(val) - playerData = val -end) - -function Bridge.getJob() - local job = playerData['job'] - return job.name, job.grade.level -end - -RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() - -end) - -RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) - -end) - -RegisterNetEvent('QBCore:Client:OnGangUpdate', function(GangInfo) - -end) - -RegisterNetEvent('QBCore:Player:SetPlayerData', function(val) - -end) - -local function exportHandler(exportName, func) - AddEventHandler(('__cfx_export_qb-target_%s'):format(exportName), function(setCB) - setCB(func) - end) -end - -exportHandler('AddTargetEntity', function(entities, data) - local menu = { - entity = entities, - options = {}, - maxDistance = data.distance or 3 - } - - if type(entities) == 'table' then - - else - for key, value in pairs(data.options) do - local option = {} - menu.options[#menu.options + 1] = option - - if value.canInteract then - option['canInteract'] = value.canInteract - end - - if value.action then - option['action'] = { - type = 'sync', - func = value.action - } - end - - option['icon'] = value.icon - option['label'] = value.label - end - end - - exports['interactionMenu']:Create(menu) -end) - -exportHandler('AddBoxZone', function(name, center, length, width, options, targetoptions) - local menu = { - id = name, - position = center, - options = {}, - maxDistance = targetoptions.distance or 3 - } - - for key, value in pairs(targetoptions.options) do - local option = {} - menu.options[#menu.options + 1] = option - - if value.canInteract then - option['canInteract'] = value.canInteract - end - - if value.action then - option['action'] = { - type = 'sync', - func = value.action - } - elseif value.event then - option['event'] = { - type = value['type'], - name = value['event'] - } - end - - option['icon'] = value.icon - option['label'] = value.label - end - - exports['interactionMenu']:Create(menu) -end) diff --git a/interactionMenu/lua/providers/qb-target.lua b/interactionMenu/lua/providers/qb-target.lua new file mode 100644 index 0000000..60eb197 --- /dev/null +++ b/interactionMenu/lua/providers/qb-target.lua @@ -0,0 +1,312 @@ +if GetResourceState('qb-core') ~= 'started' then return end + +local function replaceExport(exportName, func) + Util.replaceExport('qb-target', exportName, func) +end + +local function convertTargetOptions(targetOptions) + targetOptions = targetOptions.options + local menuOptions = {} + + for id, value in pairs(targetOptions) do + local payload + local option = { + icon = value.icon, + label = value.label, + canInteract = value.canInteract, + order = value.num, + job = value.job, + gang = value.gang + } + + -- Move other properties to `payload` + for key, val in pairs(value) do + if not option[key] and key ~= "event" and key ~= "action" and key ~= 'type' then + if not payload then payload = {} end + payload[key] = val + end + end + + if value.action then + option.action = value.action + elseif value.event then + option.event = { + type = value.type, + name = value.event, + payload = payload + } + end + + menuOptions[#menuOptions + 1] = option + end + + -- #TODO: sort by order/num + + return menuOptions +end + +replaceExport('AddCircleZone', function(name, center, radius, options, targetoptions) + local distance = targetoptions.distance or 3 + local menu = { + id = name, + position = center, + options = convertTargetOptions(targetoptions), + maxDistance = distance, + schemaType = 'qbtarget' + } + + return exports['interactionMenu']:Create(menu) +end) + +replaceExport('AddBoxZone', function(name, center, length, width, options, targetoptions) + local distance = targetoptions.distance or 3 + local menu = { + id = name, + position = center, + options = convertTargetOptions(targetoptions), + maxDistance = distance, + schemaType = 'qbtarget' + } + + return exports['interactionMenu']:Create(menu) +end) + +local function getCentroid(polygon) + local centroid = vec3(0, 0, 0) + local numPoints = #polygon + + for i = 1, numPoints do + centroid = centroid + polygon[i] + end + + return (centroid / numPoints) +end + +replaceExport('AddPolyZone', function(name, points, options, targetoptions) + -- #TODO: fix: or just set it to not supported export + local newPoints = table.create(#points, 0) + local thickness = math.abs(options.maxZ - options.minZ) + local distance = targetoptions.distance or 3 + + local menu = { + id = name, + position = vec3(0, 0, 0), + options = convertTargetOptions(targetoptions), + maxDistance = distance, + schemaType = 'qbtarget' + } + + for i = 1, #points do + local point = points[i] + newPoints[i] = vec3(point.x, point.y, options.maxZ - (thickness / 2)) + end + + menu.center = getCentroid(newPoints) + return exports['interactionMenu']:Create(menu) +end) + +replaceExport('RemoveZone', function(id) + exports['interactionMenu']:remove(id) +end) + +replaceExport('AddTargetBone', function(bones, options) + bones = Util.ensureTable(bones) + local invokingResource = GetInvokingResource() + + for _, bone in ipairs(bones) do + local id = invokingResource .. '_' .. bone + + exports['interactionMenu']:createGlobal { + id = id, + type = 'bones', + bone = bone, + offset = vec3(0, 0, 0), + maxDistance = 1.0, + options = convertTargetOptions(options), + schemaType = 'qbtarget' + } + end +end) + +replaceExport('RemoveTargetBone', function(bones, labels) + bones = Util.ensureTable(bones) + local invokingResource = GetInvokingResource() + + for i = 1, #bones do + local bone = bones[i] + local id = invokingResource .. '_' .. bone + + exports['interactionMenu']:remove(id) + end +end) + +replaceExport('AddTargetEntity', function(entities, options) + entities = Util.ensureTable(entities) + + for i = 1, #entities do + local entity = entities[i] + + if NetworkGetEntityIsNetworked(entity) then + local netId = NetworkGetNetworkIdFromEntity(entity) + + exports['interactionMenu']:Create { + id = entity, + netId = netId, + options = convertTargetOptions(options), + schemaType = 'qbtarget' + } + else + exports['interactionMenu']:Create { + id = entity, + entity = entity, + options = convertTargetOptions(options), + schemaType = 'qbtarget' + } + end + end +end) + +replaceExport('RemoveTargetEntity', function(entities, labels) + entities = Util.ensureTable(entities) + + for i = 1, #entities do + local entity = entities[i] + + if NetworkGetEntityIsNetworked(entity) then + local netId = NetworkGetNetworkIdFromEntity(entity) + exports['interactionMenu']:remove(netId) + else + exports['interactionMenu']:remove(entity) + end + end +end) + +replaceExport('AddTargetModel', function(models, options) + models = Util.ensureTable(models) + local distance = options.distance or 3 + local invokingResource = GetInvokingResource() + + for i = 1, #models do + local model = models[i] + local id = invokingResource .. '|' .. model + local modelHash = type(model) == "number" and model or joaat(model) + + exports['interactionMenu']:Create { + id = id, + model = modelHash, + maxDistance = distance, + options = convertTargetOptions(options), + schemaType = 'qbtarget' + } + end +end) + +replaceExport('RemoveTargetModel', function(models, labels) + models = Util.ensureTable(models) + local invokingResource = GetInvokingResource() + + for i = 1, #models do + local model = models[i] + local id = invokingResource .. '|' .. model + exports['interactionMenu']:remove(id) + end +end) + +local idTemplate = '%s_%s%s_%s' +local function handleGlobalEntity(action, entityType, options) + local distance = options.distance or 3 + local invokingResource = GetInvokingResource() + options = convertTargetOptions(options) + + for _, value in ipairs(options) do + local label = value.label or value + local id = idTemplate:format(invokingResource, action, entityType, Util.cleanString(label)) + + if action == 'add' then + exports['interactionMenu']:createGlobal { + id = id, + type = entityType, + offset = vec3(0, 0, 0), + maxDistance = distance, + options = { value }, + schemaType = 'qbtarget' + } + elseif action == 'remove' then + exports['interactionMenu']:remove(id) + end + end +end + +replaceExport('AddGlobalPed', function(options) + handleGlobalEntity('add', 'peds', options) +end) + +replaceExport('RemoveGlobalPed', function(labels) + handleGlobalEntity('remove', 'peds', Util.ensureTable(labels)) +end) + +replaceExport('AddGlobalVehicle', function(options) + handleGlobalEntity('add', 'vehicles', options) +end) + +replaceExport('RemoveGlobalVehicle', function(labels) + handleGlobalEntity('remove', 'vehicles', Util.ensureTable(labels)) +end) + +replaceExport('AddGlobalObject', function(options) + handleGlobalEntity('add', 'entities', options) +end) + +replaceExport('RemoveGlobalObject', function(labels) + handleGlobalEntity('remove', 'entities', Util.ensureTable(labels)) +end) + +replaceExport('AddGlobalPlayer', function(options) + handleGlobalEntity('add', 'players', options) +end) + +replaceExport('RemoveGlobalPlayer', function(labels) + handleGlobalEntity('remove', 'players', Util.ensureTable(labels)) +end) + +-- might be possible! +replaceExport('AddEntityZone') +replaceExport('DisableTarget') +replaceExport('CheckEntity') +replaceExport('CheckBones') +-- mine is always active!? +replaceExport('IsTargetActive') +replaceExport('IsTargetSuccess') + +-- bruh +replaceExport('RaycastCamera') +replaceExport('DisableNUI') +replaceExport('EnableNUI') +replaceExport('LeftTarget') +replaceExport('DrawOutlineEntity') +replaceExport('AddGlobalType') +replaceExport('RemoveGlobalType') +replaceExport('DeletePeds') +replaceExport('RemoveSpawnedPed') +replaceExport('SpawnPed') +replaceExport('GetGlobalTypeData') +replaceExport('GetZoneData') +replaceExport('GetTargetBoneData') +replaceExport('GetTargetEntityData') +replaceExport('GetTargetModelData') +replaceExport('GetGlobalPedData') +replaceExport('GetGlobalVehicleData') +replaceExport('GetGlobalObjectData') +replaceExport('GetGlobalPlayerData') +replaceExport('UpdateGlobalTypeData') +replaceExport('UpdateZoneData') +replaceExport('UpdateTargetBoneData') +replaceExport('UpdateTargetEntityData') +replaceExport('UpdateTargetModelData') +replaceExport('UpdateGlobalPedData') +replaceExport('UpdateGlobalVehicleData') +replaceExport('UpdateGlobalObjectData') +replaceExport('UpdateGlobalPlayerData') +replaceExport('GetPeds') +replaceExport('UpdatePedsData') +replaceExport('AllowTargeting') diff --git a/interactionMenu/lua/providers/qb-target_test.lua b/interactionMenu/lua/providers/qb-target_test.lua new file mode 100644 index 0000000..5ee423f --- /dev/null +++ b/interactionMenu/lua/providers/qb-target_test.lua @@ -0,0 +1,324 @@ +if not Config.devMode then return end +if GetResourceState('qb-core') ~= 'started' then return end + +local function AddBoxZone() + local k = 1 + local v = { + position = vector4(-2037.4, 3191.8, 32.81, 47.71), + length = 1, + width = 2 + } + exports["qb-target"]:AddBoxZone("Bank_" .. k, v.position, v.length, v.width, { + name = "Bank_" .. k, + heading = v.heading, + minZ = v.minZ, + maxZ = v.maxZ, + debugPoly = Config.debugPoly + }, { + options = { + { + type = "client", + event = "qb-banking:openBankScreen", + icon = "fas fa-university", + label = "Access Bank", + } + }, + distance = 1.5 + }) +end + +local function AddCircleZone() + local i = 1 + local v = vector3(-2036.28, 3191.27, 32.81) + exports['qb-target']:AddCircleZone('PoliceDuty_' .. i, vector3(v.x, v.y, v.z), 0.5, { + name = 'PoliceDuty_' .. i, + useZ = true, + debugPoly = Config.debugPoly, + }, { + options = { + { + type = 'client', + event = 'qb-policejob:ToggleDuty', + icon = 'fas fa-sign-in-alt', + label = 'Sign In', + jobType = 'leo', + }, + }, + distance = 1.5 + }) + v = vector4(-2035.55, 3192.14, 32.81, 239.09) + exports['qb-target']:AddCircleZone('PoliceTrash_' .. i, vector3(v.x, v.y, v.z), 0.5, { + name = 'PoliceTrash_' .. i, + useZ = true, + debugPoly = Config.debugPoly, + }, { + options = { + { + type = 'server', + event = 'qb-policejob:server:trash', + icon = 'fas fa-trash', + label = "Open Bin", + jobType = 'leo', + }, + }, + distance = 1.5 + }) +end + +local function AddTargetBone() + local bones = { + 'platelight', + 'exhaust' + } + exports['qb-target']:AddTargetBone(bones, { + options = { + { + num = 1, + icon = 'fa-solid fa-car', + label = 'Scan Plate', + action = function(entity) + print("Plate Scan:", entity) + end, + job = 'police', + } + }, + distance = 4.0, + }) + + local wheels = { + "wheel_lf", + "wheel_rf", + "wheel_lm1", + "wheel_rm1", + "wheel_lm2", + "wheel_rm2", + "wheel_lm3", + "wheel_rm3", + "wheel_lr", + "wheel_rr", + } + exports['qb-target']:AddTargetBone(wheels, { + options = { + { + event = "qb-target_test:client:wheel", + icon = "fas fa-wrench", + label = "Adjust", + item = 'iron', + }, + }, + distance = 1.5 + }) + + Wait(5000) + exports['qb-target']:RemoveTargetBone(wheels) + AddEventHandler("qb-target_test:client:wheel", function(data) + print('qb-target_test:client:wheel') + end) +end + +local function AddTargetEntity() + local p1 = vector4(-2036.35, 3195.72, 31.81, 241.25) + local p2 = vector4(-2035.67, 3196.93, 31.81, 274.3) + local ped1 = Util.spawnPed(`g_m_y_famdnf_01`, p1) + local ped2 = Util.spawnPed(`g_m_y_famdnf_01`, p2) + exports['qb-target']:AddTargetEntity({ + ped1, ped2 + }, { + options = { + { + label = "Ped", + icon = "fas fa-eye", + action = function(data) + Util.print_table(data) + end, + }, + } + }) + + -- Wait(5000) + -- exports['qb-target']:RemoveTargetEntity(ped1) + -- exports['qb-target']:RemoveTargetEntity(ped2) +end + +local function AddTargetModel() + Config.Dumpsters = { 218085040, 666561306, -58485588, -206690185, 1511880420, 682791951 } + exports['qb-target']:AddTargetModel(Config.Dumpsters, { + options = { + { + event = "qb-target_test:client:dumpster", + icon = "fas fa-dumpster", + label = "Search Dumpster", + }, + }, + distance = 2 + }) + AddEventHandler('qb-target_test:client:dumpster', function(data) + print("qb-target_test:client:dumpster") + end) + + Wait(5000) + + exports['qb-target']:RemoveTargetModel(Config.Dumpsters) +end + +local function AddGlobalPed() + local options = { + options = { + { + icon = 'fas fa-dumpster', + label = 'Global ped test', + canInteract = function(entity) + return true + end, + action = function() + + end + }, + { + icon = 'fas fa-dumpster', + label = 'Global ped test 2', + canInteract = function(entity) + return true + end, + action = function() + + end + }, + }, + distance = 5, + } + + exports['qb-target']:AddGlobalPed(options) + + Wait(5000) + + exports['qb-target']:RemoveGlobalPed('Global ped test 2') + exports['qb-target']:RemoveGlobalPed({ 'Global ped test' }) +end + +local function Debug() + CreateThread(function() + Wait(1000) + + local currentResourceName = GetCurrentResourceName() + local targeting = exports['qb-target'] + + AddEventHandler(currentResourceName .. ':debug', function(data) + local entity = data.entity + local model = GetEntityModel(entity) + local type = GetEntityType(entity) + + print('Entity: ' .. entity, 'Model: ' .. model, 'Type: ' .. type) + if data.remove then + targeting:RemoveTargetEntity(data.entity, 'Hello World') + else + targeting:AddTargetEntity(data.entity, { + options = { + { + type = "client", + event = currentResourceName .. ':debug', + icon = "fas fa-circle-check", + label = "Hello World", + remove = true + }, + }, + distance = 3.0 + }) + end + end) + + targeting:AddGlobalPed({ + options = { + { + type = "client", + event = currentResourceName .. ':debug', + icon = "fas fa-male", + label = "(Debug) Ped", + }, + }, + distance = Config.MaxDistance + }) + + targeting:AddGlobalVehicle({ + options = { + { + type = "client", + event = currentResourceName .. ':debug', + icon = "fas fa-car", + label = "(Debug) Vehicle", + }, + }, + distance = Config.MaxDistance + }) + + targeting:AddGlobalObject({ + options = { + { + type = "client", + event = currentResourceName .. ':debug', + icon = "fas fa-cube", + label = "(Debug) Object", + }, + }, + distance = Config.MaxDistance + }) + + targeting:AddGlobalPlayer({ + options = { + { + type = "client", + event = currentResourceName .. ':debug', + icon = "fas fa-cube", + label = "(Debug) Player", + }, + }, + distance = Config.MaxDistance + }) + end) +end + +local function OptionsInnerProperties() + local k = 'normal' + local v = { + coords = vector4(-1996.94, 3195.34, 32.81, 278.35), + label = '24/7 Supermarket', + targetIcon = 'fas fa-shopping-basket', + targetLabel = 'Open Shop', + requiredJob = 'police' + } + exports['qb-target']:AddCircleZone(k, vector3(v.coords.x, v.coords.y, v.coords.z), 0.5, { + name = k, + debugPoly = false, + useZ = true + }, { + options = { + { + label = v.targetLabel, + icon = v.targetIcon, + item = v.requiredItem, + shop = k, + job = v.requiredJob, + gang = v.requiredGang, + action = function() + print('test') + end + } + }, + distance = 2.0 + }) +end + +-- CreateThread(function() +-- Wait(1000) +-- print('Starting qb-target tests') + +-- AddCircleZone() +-- AddBoxZone() +-- AddTargetBone() +-- AddTargetEntity() +-- AddTargetModel() +-- AddGlobalPed() +-- Debug() +-- OptionsInnerProperties() +-- exports['qb-target']:RaycastCamera() +-- end)