Repository: overextended/ox_target
Branch: main
Commit: abe153aa3370
Files: 45
Total size: 74.4 KB
Directory structure:
gitextract_ba2qc7hr/
├── .github/
│ ├── actions/
│ │ └── bump-manifest-version.js
│ └── workflows/
│ └── release.yml
├── LICENSE
├── README.md
├── client/
│ ├── api.lua
│ ├── compat/
│ │ └── qtarget.lua
│ ├── debug.lua
│ ├── defaults.lua
│ ├── framework/
│ │ ├── esx.lua
│ │ ├── nd.lua
│ │ ├── ox.lua
│ │ └── qbx.lua
│ ├── main.lua
│ ├── state.lua
│ └── utils.lua
├── fxmanifest.lua
├── locales/
│ ├── cs.json
│ ├── da.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── et.json
│ ├── fi.json
│ ├── fr.json
│ ├── hr.json
│ ├── hu.json
│ ├── id.json
│ ├── it.json
│ ├── nl.json
│ ├── no.json
│ ├── pl.json
│ ├── pt-br.json
│ ├── pt.json
│ ├── ro.json
│ ├── sl.json
│ ├── sv.json
│ ├── tr.json
│ ├── zh-cn.json
│ └── zh-tw.json
├── server/
│ └── main.lua
└── web/
├── index.html
├── js/
│ ├── createOptions.js
│ ├── fetchNui.js
│ └── main.js
└── style.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/actions/bump-manifest-version.js
================================================
const fxManifest = await Bun.file('./fxmanifest.lua').text();
let newVersion = process.env.TGT_RELEASE_VERSION;
newVersion = newVersion.replace('v', '')
const newFileContent = fxManifest.replace(/\bversion\s+(.*)$/gm, `version '${newVersion}'`);
await Bun.write('./fxmanifest.lua', newFileContent);
================================================
FILE: .github/workflows/release.yml
================================================
name: Create release
on:
push:
tags:
- "v*.*.*"
jobs:
create-release:
if: github.actor_id != 278903378
runs-on: ubuntu-latest
steps:
- name: Install zip
run: sudo apt install zip
- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Generate GitHub App token
id: app_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Get latest code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
token: ${{ steps.app_token.outputs.token }}
- name: Bump manifest version
run: bun run .github/actions/bump-manifest-version.js
env:
TGT_RELEASE_VERSION: ${{ github.ref_name }}
- name: Push version bump change
uses: EndBug/add-and-commit@v9
with:
add: fxmanifest.lua
push: true
default_author: github_actions
message: "chore: bump version to ${{ github.ref_name }}"
- name: Bundle files
run: |
mkdir -p ./temp/ox_target
cp ./{LICENSE,README.md,fxmanifest.lua} ./temp/ox_target
cp -r ./{client,server,web,locales} ./temp/ox_target
cd ./temp && zip -r ../ox_target.zip ./ox_target
- name: Create release
uses: "marvinpinto/action-automatic-releases@v1.2.1"
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
prerelease: false
files: ox_target.zip
- name: Update tag
uses: EndBug/latest-tag@v1
with:
ref: ${{ github.ref_name }}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Overextended
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ox_target




A performant and flexible standalone "third-eye" targeting resource, with additional functionality for supported frameworks.
ox_target is the successor to qtarget, which was a mostly-compatible fork of bt-target.
To improve many design flaws, ox_target has been written from scratch and drops support for bt-target/qtarget standards, though partial compatibility is being implemented where possible.
## 📚 Documentation
https://overextended.dev/ox_target
## 💾 Download
https://github.com/overextended/ox_target/releases/latest/download/ox_target.zip
## ✨ Features
- Improved entity and world collision than its predecessor.
- Improved error handling when running external code.
- Menus for nested target options.
- Partial compatibility for qtarget (the thing qb-target is based on, I made the original idiots).
- Registering options no longer overrides existing options.
- Groups and items checking for supported frameworks.
================================================
FILE: client/api.lua
================================================
---@class OxTargetOption
---@field resource? string
local utils = require 'client.utils'
local api = setmetatable({}, {
__newindex = function(self, index, value)
rawset(self, index, value)
exports(index, value)
end
})
---Throws a formatted type error
---@param variable string
---@param expected string
---@param received string
local function typeError(variable, expected, received)
error(("expected %s to have type '%s' (received %s)"):format(variable, expected, received))
end
---Checks options and throws an error on type mismatch
---@param options OxTargetOption | OxTargetOption[]
---@return OxTargetOption[]
local function checkOptions(options)
local optionsType = type(options)
if optionsType ~= 'table' then
typeError('options', 'table', optionsType)
end
local tableType = table.type(options)
if tableType == 'hash' and options.label then
options = { options }
elseif tableType ~= 'array' then
typeError('options', 'array', ('%s table'):format(tableType))
end
return options
end
---@param data OxTargetPolyZone | table
---@return number
function api.addPolyZone(data)
if data.debug then utils.warn('Creating new PolyZone with debug enabled.') end
data.resource = GetInvokingResource()
data.options = checkOptions(data.options)
return lib.zones.poly(data).id
end
---@param data OxTargetBoxZone | table
---@return number
function api.addBoxZone(data)
if data.debug then utils.warn('Creating new BoxZone with debug enabled.') end
data.resource = GetInvokingResource()
data.options = checkOptions(data.options)
return lib.zones.box(data).id
end
---@param data OxTargetSphereZone | table
---@return number
function api.addSphereZone(data)
if data.debug then utils.warn('Creating new SphereZone with debug enabled.') end
data.resource = GetInvokingResource()
data.options = checkOptions(data.options)
return lib.zones.sphere(data).id
end
---@param id number | string The ID of the zone to check. It can be either a number or a string representing the zone's index or name, respectively.
---@return boolean returns true if the zone with the specified ID exists, otherwise false.
function api.zoneExists(id)
if not Zones or (type(id) ~= 'number' and type(id) ~= 'string') then return false end
if type(id) == 'number' and Zones[id] then return true end
for _, zone in pairs(lib.zones.getAllZones()) do
if type(id) == 'string' and zone.name == id then return true end
end
return false
end
---@param id number | string
---@param suppressWarning boolean?
function api.removeZone(id, suppressWarning)
if Zones then
if type(id) == 'string' then
local foundZone
for _, v in pairs(lib.zones.getAllZones()) do
if v.name == id then
foundZone = true
v:remove()
end
end
if foundZone then return end
elseif Zones[id] then
return Zones[id]:remove()
end
end
if suppressWarning then return end
warn(('attempted to remove a zone that does not exist (id: %s)'):format(id))
end
---@param target table
---@param remove string | string[]
---@param resource string
---@param showWarning? boolean
local function removeTarget(target, remove, resource, showWarning)
if type(remove) ~= 'table' then remove = { remove } end
for i = #target, 1, -1 do
local option = target[i]
if option.resource == resource then
for j = #remove, 1, -1 do
if option.name == remove[j] then
table.remove(target, i)
if showWarning then
utils.warn(("Replacing existing target option '%s'."):format(option.name))
end
end
end
end
end
end
---@param target table
---@param options OxTargetOption | OxTargetOption[]
---@param resource string
local function addTarget(target, options, resource)
options = checkOptions(options)
local checkNames = {}
resource = resource or 'ox_target'
for i = 1, #options do
local option = options[i]
option.resource = resource
if option.name then
checkNames[#checkNames + 1] = option.name
end
end
if checkNames[1] then
removeTarget(target, checkNames, resource, true)
end
local num = #target
for i = 1, #options do
local option = options[i]
if resource == 'ox_target' then
if option.canInteract then
option.canInteract = msgpack.unpack(msgpack.pack(option.canInteract))
end
if option.onSelect then
option.onSelect = msgpack.unpack(msgpack.pack(option.onSelect))
end
end
num += 1
target[num] = options[i]
end
end
---@type table<number, OxTargetOption[]>
local peds = {}
---@param options OxTargetOption | OxTargetOption[]
function api.addGlobalPed(options)
addTarget(peds, options, GetInvokingResource())
end
---@param options string | string[]
function api.removeGlobalPed(options)
removeTarget(peds, options, GetInvokingResource())
end
---@type table<number, OxTargetOption[]>
local vehicles = {}
---@param options OxTargetOption | OxTargetOption[]
function api.addGlobalVehicle(options)
addTarget(vehicles, options, GetInvokingResource())
end
---@param options string | string[]
function api.removeGlobalVehicle(options)
removeTarget(vehicles, options, GetInvokingResource())
end
---@type table<number, OxTargetOption[]>
local objects = {}
---@param options OxTargetOption | OxTargetOption[]
function api.addGlobalObject(options)
addTarget(objects, options, GetInvokingResource())
end
---@param options string | string[]
function api.removeGlobalObject(options)
removeTarget(objects, options, GetInvokingResource())
end
---@type table<number, OxTargetOption[]>
local players = {}
---@param options OxTargetOption | OxTargetOption[]
function api.addGlobalPlayer(options)
addTarget(players, options, GetInvokingResource())
end
---@param options string | string[]
function api.removeGlobalPlayer(options)
removeTarget(players, options, GetInvokingResource())
end
---@type table<number, OxTargetOption[]>
local models = {}
---@param arr (number | string) | (number | string)[]
---@param options OxTargetOption | OxTargetOption[]
function api.addModel(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local model = arr[i]
model = tonumber(model) or joaat(model)
if not models[model] then
models[model] = {}
end
addTarget(models[model], options, resource)
end
end
---@param arr (number | string) | (number | string)[]
---@param options? string | string[]
function api.removeModel(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local model = arr[i]
model = tonumber(model) or joaat(model)
if models[model] then
if options then
removeTarget(models[model], options, resource)
end
if not options or #models[model] == 0 then
models[model] = nil
end
end
end
end
---@type table<number, OxTargetOption[]>
local entities = {}
---@param arr number | number[]
---@param options OxTargetOption | OxTargetOption[]
function api.addEntity(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local netId = arr[i]
if NetworkDoesNetworkIdExist(netId) then
if not entities[netId] then
entities[netId] = {}
if not Entity(NetworkGetEntityFromNetworkId(netId)).state.hasTargetOptions then
TriggerServerEvent('ox_target:setEntityHasOptions', netId)
end
end
addTarget(entities[netId], options, resource)
end
end
end
---@param arr number | number[]
---@param options? string | string[]
function api.removeEntity(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local netId = arr[i]
if entities[netId] then
if options then
removeTarget(entities[netId], options, resource)
end
if not options or #entities[netId] == 0 then
entities[netId] = nil
end
end
end
end
RegisterNetEvent('ox_target:removeEntity', api.removeEntity)
---@type table<number, OxTargetOption[]>
local localEntities = {}
---@param arr number | number[]
---@param options OxTargetOption | OxTargetOption[]
function api.addLocalEntity(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local entityId = arr[i]
if DoesEntityExist(entityId) then
if not localEntities[entityId] then
localEntities[entityId] = {}
end
addTarget(localEntities[entityId], options, resource)
else
lib.print.warn(("No entity with id '%s' exists in %s."):format(entityId, resource))
end
end
end
---@param arr number | number[]
---@param options? table
function api.removeLocalEntity(arr, options)
if type(arr) ~= 'table' then arr = { arr } end
local resource = GetInvokingResource()
for i = 1, #arr do
local entity = arr[i]
if localEntities[entity] then
if options then
removeTarget(localEntities[entity], options, resource)
end
if not options or #localEntities[entity] == 0 then
localEntities[entity] = nil
end
end
end
end
CreateThread(function()
while true do
Wait(60000)
for entityId in pairs(localEntities) do
if not DoesEntityExist(entityId) then
localEntities[entityId] = nil
end
end
end
end)
---@param resource string
---@param target table
local function removeResourceGlobals(resource, target)
for i = 1, #target do
local options = target[i]
for j = #options, 1, -1 do
if options[j].resource == resource then
table.remove(options, j)
end
end
end
end
---@param resource string
---@param target table
local function removeResourceTargets(resource, target)
for i = 1, #target do
local tbl = target[i]
for key, options in pairs(tbl) do
for j = #options, 1, -1 do
if options[j].resource == resource then
table.remove(options, j)
end
end
if #options == 0 then
tbl[key] = nil
end
end
end
end
---@param resource string
AddEventHandler('onClientResourceStop', function(resource)
removeResourceGlobals(resource, { peds, vehicles, objects, players })
removeResourceTargets(resource, { models, entities, localEntities })
if Zones then
for _, v in pairs(Zones) do
if v.resource == resource then
v:remove()
end
end
end
end)
local NetworkGetEntityIsNetworked = NetworkGetEntityIsNetworked
local NetworkGetNetworkIdFromEntity = NetworkGetNetworkIdFromEntity
---@class OxTargetOptions
local options_mt = {}
options_mt.__index = options_mt
options_mt.size = 1
function options_mt:wipe()
options_mt.size = 1
self.globalTarget = nil
self.model = nil
self.entity = nil
self.localEntity = nil
if self.__global[1]?.name == 'builtin:goback' then
table.remove(self.__global, 1)
end
end
---@param entity? number
---@param _type? number
---@param model? number
function options_mt:set(entity, _type, model)
if not entity then return end
if _type == 1 and IsPedAPlayer(entity) then
self:wipe()
self.globalTarget = players
options_mt.size += 1
return
end
local netId = NetworkGetEntityIsNetworked(entity) and NetworkGetNetworkIdFromEntity(entity)
self.globalTarget = _type == 1 and peds or _type == 2 and vehicles or objects
self.model = models[model]
self.entity = netId and entities[netId] or nil
self.localEntity = localEntities[entity]
options_mt.size += 1
if self.model then options_mt.size += 1 end
if self.entity then options_mt.size += 1 end
if self.localEntity then options_mt.size += 1 end
end
---@type OxTargetOption[]
local global = {}
---@param options OxTargetOption | OxTargetOption[]
function api.addGlobalOption(options)
addTarget(global, options, GetInvokingResource())
end
---@param options string | string[]
function api.removeGlobalOption(options)
removeTarget(global, options, GetInvokingResource())
end
---@class OxTargetOptions
local options = setmetatable({
__global = global
}, options_mt)
---@param entity? number
---@param _type? number
---@param model? number
function api.getTargetOptions(entity, _type, model)
if not entity then return options end
if IsPedAPlayer(entity) then
return {
global = players,
}
end
local netId = NetworkGetEntityIsNetworked(entity) and NetworkGetNetworkIdFromEntity(entity)
return {
global = _type == 1 and peds or _type == 2 and vehicles or objects,
model = models[model],
entity = netId and entities[netId] or nil,
localEntity = localEntities[entity],
}
end
local state = require 'client.state'
function api.disableTargeting(value)
if value then
state.setActive(false)
end
state.setDisabled(value)
end
function api.isActive()
return state.isActive()
end
return api
================================================
FILE: client/compat/qtarget.lua
================================================
local function exportHandler(exportName, func)
AddEventHandler(('__cfx_export_qtarget_%s'):format(exportName), function(setCB)
setCB(func)
end)
end
---@param options table
---@return table
local function convert(options)
local distance = options.distance
options = options.options
-- People may pass options as a hashmap (or mixed, even)
for k, v in pairs(options) do
if type(k) ~= 'number' then
table.insert(options, v)
end
end
for id, v in pairs(options) do
if type(id) ~= 'number' then
options[id] = nil
goto continue
end
v.onSelect = v.action
v.distance = v.distance or distance
v.name = v.name or v.label
v.groups = v.job
v.items = v.item or v.required_item
if v.event and v.type and v.type ~= 'client' then
if v.type == 'server' then
v.serverEvent = v.event
elseif v.type == 'command' then
v.command = v.event
end
v.event = nil
v.type = nil
end
v.action = nil
v.job = nil
v.item = nil
v.required_item = nil
v.qtarget = true
::continue::
end
return options
end
local api = require 'client.api'
exportHandler('AddBoxZone', function(name, center, length, width, options, targetoptions)
local z = center.z
if not options.minZ then
options.minZ = -100
end
if not options.maxZ then
options.maxZ = 800
end
if not options.useZ then
z = z + math.abs(options.maxZ - options.minZ) / 2
center = vec3(center.x, center.y, z)
end
return api.addBoxZone({
name = name,
coords = center,
size = vec3(width, length, (options.useZ or not options.maxZ) and center.z or math.abs(options.maxZ - options.minZ)),
debug = options.debugPoly,
rotation = options.heading,
options = convert(targetoptions),
})
end)
exportHandler('AddPolyZone', function(name, points, options, targetoptions)
local newPoints = table.create(#points, 0)
local thickness = math.abs(options.maxZ - options.minZ)
for i = 1, #points do
local point = points[i]
newPoints[i] = vec3(point.x, point.y, options.maxZ - (thickness / 2))
end
return api.addPolyZone({
name = name,
points = newPoints,
thickness = thickness,
debug = options.debugPoly,
options = convert(targetoptions),
})
end)
exportHandler('AddCircleZone', function(name, center, radius, options, targetoptions)
return api.addSphereZone({
name = name,
coords = center,
radius = radius,
debug = options.debugPoly,
options = convert(targetoptions),
})
end)
exportHandler('RemoveZone', function(id)
api.removeZone(id, true)
end)
exportHandler('AddTargetBone', function(bones, options)
if type(bones) ~= 'table' then bones = { bones } end
options = convert(options)
for _, v in pairs(options) do
v.bones = bones
end
exports.ox_target:addGlobalVehicle(options)
end)
exportHandler('AddTargetEntity', function(entities, options)
if type(entities) ~= 'table' then entities = { entities } end
options = convert(options)
for i = 1, #entities do
local entity = entities[i]
if NetworkGetEntityIsNetworked(entity) then
api.addEntity(NetworkGetNetworkIdFromEntity(entity), options)
else
api.addLocalEntity(entity, options)
end
end
end)
exportHandler('RemoveTargetEntity', function(entities, labels)
if type(entities) ~= 'table' then entities = { entities } end
for i = 1, #entities do
local entity = entities[i]
if NetworkGetEntityIsNetworked(entity) then
api.removeEntity(NetworkGetNetworkIdFromEntity(entity), labels)
else
api.removeLocalEntity(entity, labels)
end
end
end)
exportHandler('AddTargetModel', function(models, options)
api.addModel(models, convert(options))
end)
exportHandler('RemoveTargetModel', function(models, labels)
api.removeModel(models, labels)
end)
exportHandler('Ped', function(options)
api.addGlobalPed(convert(options))
end)
exportHandler('RemovePed', function(labels)
api.removeGlobalPed(labels)
end)
exportHandler('Vehicle', function(options)
api.addGlobalVehicle(convert(options))
end)
exportHandler('RemoveVehicle', function(labels)
api.removeGlobalVehicle(labels)
end)
exportHandler('Object', function(options)
api.addGlobalObject(convert(options))
end)
exportHandler('RemoveObject', function(labels)
api.removeGlobalObject(labels)
end)
exportHandler('Player', function(options)
api.addGlobalPlayer(convert(options))
end)
exportHandler('RemovePlayer', function(labels)
api.removeGlobalPlayer(labels)
end)
================================================
FILE: client/debug.lua
================================================
AddEventHandler('ox_target:debug', function(data)
if data.entity and GetEntityType(data.entity) > 0 then
data.archetype = GetEntityArchetypeName(data.entity)
data.model = GetEntityModel(data.entity)
end
print(json.encode(data, {indent=true}))
end)
if GetConvarInt('ox_target:debug', 0) ~= 1 then return end
local ox_target = exports.ox_target
local drawZones = true
ox_target:addBoxZone({
coords = vec3(442.5363, -1017.666, 28.85637),
size = vec3(3, 3, 3),
rotation = 45,
debug = drawZones,
drawSprite = true,
options = {
{
name = 'debug_box',
event = 'ox_target:debug',
icon = 'fa-solid fa-cube',
label = locale('debug_box'),
}
}
})
ox_target:addSphereZone({
coords = vec3(440.5363, -1015.666, 28.85637),
radius = 3,
debug = drawZones,
drawSprite = true,
options = {
{
name = 'debug_sphere',
event = 'ox_target:debug',
icon = 'fa-solid fa-circle',
label = locale('debug_sphere'),
}
}
})
ox_target:addModel(`police`, {
{
name = 'debug_model',
event = 'ox_target:debug',
icon = 'fa-solid fa-handcuffs',
label = locale('debug_police_car'),
}
})
ox_target:addGlobalPed({
{
name = 'debug_ped',
event = 'ox_target:debug',
icon = 'fa-solid fa-male',
label = locale('debug_ped'),
}
})
ox_target:addGlobalVehicle({
{
name = 'debug_vehicle',
event = 'ox_target:debug',
icon = 'fa-solid fa-car',
label = locale('debug_vehicle'),
}
})
ox_target:addGlobalObject({
{
name = 'debug_object',
event = 'ox_target:debug',
icon = 'fa-solid fa-bong',
label = locale('debug_object'),
}
})
ox_target:addGlobalOption({
{
name = 'debug_global',
icon = 'fa-solid fa-globe',
label = locale('debug_global'),
openMenu = 'debug_global'
}
})
ox_target:addGlobalOption({
{
name = 'debug_global2',
event = 'ox_target:debug',
icon = 'fa-solid fa-globe',
label = locale('debug_global') .. ' 2',
menuName = 'debug_global'
}
})
================================================
FILE: client/defaults.lua
================================================
if GetConvarInt('ox_target:defaults', 1) ~= 1 then return end
local api = require 'client.api'
local GetEntityBoneIndexByName = GetEntityBoneIndexByName
local GetEntityBonePosition_2 = GetEntityBonePosition_2
local GetVehicleDoorLockStatus = GetVehicleDoorLockStatus
local bones = {
[0] = 'dside_f',
[1] = 'pside_f',
[2] = 'dside_r',
[3] = 'pside_r'
}
---@param vehicle number
---@param door number
local function toggleDoor(vehicle, door)
if GetVehicleDoorLockStatus(vehicle) ~= 2 then
if GetVehicleDoorAngleRatio(vehicle, door) > 0.0 then
SetVehicleDoorShut(vehicle, door, false)
else
SetVehicleDoorOpen(vehicle, door, false, false)
end
end
end
---@param entity number
---@param coords vector3
---@param door number
---@param useOffset boolean?
---@return boolean?
local function canInteractWithDoor(entity, coords, door, useOffset)
if not GetIsDoorValid(entity, door) or GetVehicleDoorLockStatus(entity) > 1 or IsVehicleDoorDamaged(entity, door) or cache.vehicle then return end
if useOffset then return true end
local boneName = bones[door]
if not boneName then return false end
local boneId = GetEntityBoneIndexByName(entity, 'door_' .. boneName)
if boneId ~= -1 then
return #(coords - GetEntityBonePosition_2(entity, boneId)) < 0.5 or
#(coords - GetEntityBonePosition_2(entity, GetEntityBoneIndexByName(entity, 'seat_' .. boneName))) < 0.72
end
end
local function onSelectDoor(data, door)
local entity = data.entity
if NetworkGetEntityOwner(entity) == cache.playerId then
return toggleDoor(entity, door)
end
TriggerServerEvent('ox_target:toggleEntityDoor', VehToNet(entity), door)
end
RegisterNetEvent('ox_target:toggleEntityDoor', function(netId, door)
local entity = NetToVeh(netId)
toggleDoor(entity, door)
end)
api.addGlobalVehicle({
{
name = 'ox_target:driverF',
icon = 'fa-solid fa-car-side',
label = locale('toggle_front_driver_door'),
bones = { 'door_dside_f', 'seat_dside_f' },
distance = 2,
canInteract = function(entity, distance, coords, name)
return canInteractWithDoor(entity, coords, 0)
end,
onSelect = function(data)
onSelectDoor(data, 0)
end
},
{
name = 'ox_target:passengerF',
icon = 'fa-solid fa-car-side',
label = locale('toggle_front_passenger_door'),
bones = { 'door_pside_f', 'seat_pside_f' },
distance = 2,
canInteract = function(entity, distance, coords, name)
return canInteractWithDoor(entity, coords, 1)
end,
onSelect = function(data)
onSelectDoor(data, 1)
end
},
{
name = 'ox_target:driverR',
icon = 'fa-solid fa-car-side',
label = locale('toggle_rear_driver_door'),
bones = { 'door_dside_r', 'seat_dside_r' },
distance = 2,
canInteract = function(entity, distance, coords)
return canInteractWithDoor(entity, coords, 2)
end,
onSelect = function(data)
onSelectDoor(data, 2)
end
},
{
name = 'ox_target:passengerR',
icon = 'fa-solid fa-car-side',
label = locale('toggle_rear_passenger_door'),
bones = { 'door_pside_r', 'seat_pside_r' },
distance = 2,
canInteract = function(entity, distance, coords)
return canInteractWithDoor(entity, coords, 3)
end,
onSelect = function(data)
onSelectDoor(data, 3)
end
},
{
name = 'ox_target:bonnet',
icon = 'fa-solid fa-car',
label = locale('toggle_hood'),
offset = vec3(0.5, 1, 0.5),
distance = 2,
canInteract = function(entity, distance, coords)
return canInteractWithDoor(entity, coords, 4, true)
end,
onSelect = function(data)
onSelectDoor(data, 4)
end
},
{
name = 'ox_target:trunk',
icon = 'fa-solid fa-car-rear',
label = locale('toggle_trunk'),
offset = vec3(0.5, 0, 0.5),
distance = 2,
canInteract = function(entity, distance, coords, name)
return canInteractWithDoor(entity, coords, 5, true)
end,
onSelect = function(data)
onSelectDoor(data, 5)
end
}
})
================================================
FILE: client/framework/esx.lua
================================================
local ESX = exports.es_extended:getSharedObject()
local utils = require 'client.utils'
local groups = { 'job', 'job2' }
local playerGroups = {}
local playerItems = utils.getItems()
local usingOxInventory = utils.hasExport('ox_inventory.Items')
local function setPlayerData(playerData)
table.wipe(playerGroups)
table.wipe(playerItems)
for i = 1, #groups do
local group = groups[i]
local data = playerData[group]
if data then
playerGroups[group] = data
end
end
if usingOxInventory or not playerData.inventory then return end
for _, v in pairs(playerData.inventory) do
if v.count > 0 then
playerItems[v.name] = v.count
end
end
end
if ESX.PlayerLoaded then
setPlayerData(ESX.PlayerData)
end
RegisterNetEvent('esx:playerLoaded', function(data)
if source == '' then return end
setPlayerData(data)
end)
RegisterNetEvent('esx:setJob', function(job)
if source == '' then return end
playerGroups.job = job
end)
RegisterNetEvent('esx:setJob2', function(job)
if source == '' then return end
playerGroups.job2 = job
end)
RegisterNetEvent('esx:addInventoryItem', function(name, count)
playerItems[name] = count
end)
RegisterNetEvent('esx:removeInventoryItem', function(name, count)
playerItems[name] = count
end)
---@diagnostic disable-next-line: duplicate-set-field
function utils.hasPlayerGotGroup(filter)
local _type = type(filter)
for i = 1, #groups do
local group = groups[i]
if _type == 'string' then
local data = playerGroups[group]
if filter == data?.name then
return true
end
elseif _type == 'table' then
local tabletype = table.type(filter)
if tabletype == 'hash' then
for name, grade in pairs(filter) do
local data = playerGroups[group]
if data?.name == name and grade <= data.grade then
return true
end
end
elseif tabletype == 'array' then
for j = 1, #filter do
local name = filter[j]
local data = playerGroups[group]
if data?.name == name then
return true
end
end
end
end
end
end
================================================
FILE: client/framework/nd.lua
================================================
local NDCore = exports["ND_Core"]
local playerGroups = NDCore:getPlayer()?.groups or {}
RegisterNetEvent("ND:characterLoaded", function(data)
playerGroups = data.groups
end)
RegisterNetEvent("ND:updateCharacter", function(data)
if source == '' then return end
playerGroups = data.groups or {}
end)
local utils = require 'client.utils'
---@diagnostic disable-next-line: duplicate-set-field
function utils.hasPlayerGotGroup(filter)
local _type = type(filter)
if _type == 'string' then
local group = playerGroups[filter]
if group then
return true
end
elseif _type == 'table' then
local tabletype = table.type(filter)
if tabletype == 'hash' then
for name, grade in pairs(filter) do
local playerGrade = playerGroups[name]?.rank
if playerGrade and grade <= playerGrade then
return true
end
end
elseif tabletype == 'array' then
for i = 1, #filter do
local name = filter[i]
local group = playerGroups[name]
if group then
return true
end
end
end
end
end
================================================
FILE: client/framework/ox.lua
================================================
if not lib.checkDependency('ox_core', '0.21.3', true) then return end
local Ox = require '@ox_core.lib.init' --[[@as OxClient]]
local utils = require 'client.utils'
local player = Ox.GetPlayer()
---@diagnostic disable-next-line: duplicate-set-field
function utils.hasPlayerGotGroup(filter)
return player.getGroup(filter)
end
================================================
FILE: client/framework/qbx.lua
================================================
if not lib.checkDependency('qbx_core', '1.18.0', true) then return end
local QBX = exports.qbx_core
local utils = require 'client.utils'
---@diagnostic disable-next-line: duplicate-set-field
function utils.hasPlayerGotGroup(filter)
return QBX:HasGroup(filter)
end
================================================
FILE: client/main.lua
================================================
if not lib.checkDependency('ox_lib', '3.30.0', true) then return end
lib.locale()
local utils = require 'client.utils'
local state = require 'client.state'
local options = require 'client.api'.getTargetOptions()
require 'client.debug'
require 'client.defaults'
require 'client.compat.qtarget'
local SendNuiMessage = SendNuiMessage
local GetEntityCoords = GetEntityCoords
local GetEntityType = GetEntityType
local HasEntityClearLosToEntity = HasEntityClearLosToEntity
local GetEntityBoneIndexByName = GetEntityBoneIndexByName
local GetEntityBonePosition_2 = GetEntityBonePosition_2
local GetEntityModel = GetEntityModel
local IsDisabledControlJustPressed = IsDisabledControlJustPressed
local DisableControlAction = DisableControlAction
local DisablePlayerFiring = DisablePlayerFiring
local GetModelDimensions = GetModelDimensions
local GetOffsetFromEntityInWorldCoords = GetOffsetFromEntityInWorldCoords
local currentTarget = {}
local currentMenu
local menuChanged
local menuHistory = {}
local nearbyZones
-- Toggle ox_target, instead of holding the hotkey
local toggleHotkey = GetConvarInt('ox_target:toggleHotkey', 0) == 1
local mouseButton = GetConvarInt('ox_target:leftClick', 1) == 1 and 24 or 25
local debug = GetConvarInt('ox_target:debug', 0) == 1
local vec0 = vec3(0, 0, 0)
---@param option OxTargetOption
---@param distance number
---@param endCoords vector3
---@param entityHit? number
---@param entityType? number
---@param entityModel? number | false
local function shouldHide(option, distance, endCoords, entityHit, entityType, entityModel)
if option.menuName ~= currentMenu then
return true
end
if distance > (option.distance or 7) then
return true
end
if option.groups and not utils.hasPlayerGotGroup(option.groups) then
return true
end
if option.items and not utils.hasPlayerGotItems(option.items, option.anyItem) then
return true
end
local bone = entityModel and option.bones or nil
if bone then
---@cast entityHit number
---@cast entityType number
---@cast entityModel number
local _type = type(bone)
if _type == 'string' then
local boneId = GetEntityBoneIndexByName(entityHit, bone)
if boneId ~= -1 and #(endCoords - GetEntityBonePosition_2(entityHit, boneId)) <= 2 then
bone = boneId
else
return true
end
elseif _type == 'table' then
local closestBone, boneDistance
for j = 1, #bone do
local boneId = GetEntityBoneIndexByName(entityHit, bone[j])
if boneId ~= -1 then
local dist = #(endCoords - GetEntityBonePosition_2(entityHit, boneId))
if dist <= (boneDistance or 1) then
closestBone = boneId
boneDistance = dist
end
end
end
if closestBone then
bone = closestBone
else
return true
end
end
end
local offset = entityModel and option.offset or nil
if offset then
---@cast entityHit number
---@cast entityType number
---@cast entityModel number
if not option.absoluteOffset then
local min, max = GetModelDimensions(entityModel)
offset = (max - min) * offset + min
end
offset = GetOffsetFromEntityInWorldCoords(entityHit, offset.x, offset.y, offset.z)
if #(endCoords - offset) > (option.offsetSize or 1) then
return true
end
end
if option.canInteract then
local success, resp = pcall(option.canInteract, entityHit, distance, endCoords, option.name, bone)
return not success or not resp
end
end
local function startTargeting()
if state.isDisabled() or state.isActive() or IsNuiFocused() or IsPauseMenuActive() then return end
state.setActive(true)
local flag = 511
local hit, entityHit, endCoords, distance, lastEntity, entityType, entityModel, hasTarget, zonesChanged
local zones = {}
CreateThread(function()
local dict, texture = utils.getTexture()
local lastCoords
while state.isActive() do
lastCoords = endCoords == vec0 and lastCoords or endCoords or vec0
if debug then
DrawMarker(28, lastCoords.x, lastCoords.y, lastCoords.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.2,
0.2,
---@diagnostic disable-next-line: param-type-mismatch
255, 42, 24, 100, false, false, 0, true, false, false, false)
end
utils.drawZoneSprites(dict, texture)
DisablePlayerFiring(cache.playerId, true)
DisableControlAction(0, 25, true)
DisableControlAction(0, 140, true)
DisableControlAction(0, 141, true)
DisableControlAction(0, 142, true)
if state.isNuiFocused() then
DisableControlAction(0, 1, true)
DisableControlAction(0, 2, true)
if not hasTarget or options and IsDisabledControlJustPressed(0, 25) then
state.setNuiFocus(false, false)
end
elseif hasTarget and IsDisabledControlJustPressed(0, mouseButton) then
state.setNuiFocus(true, true)
end
Wait(0)
end
SetStreamedTextureDictAsNoLongerNeeded(dict)
end)
while state.isActive() do
if not state.isNuiFocused() and lib.progressActive() then
state.setActive(false)
break
end
local playerCoords = GetEntityCoords(cache.ped)
hit, entityHit, endCoords = lib.raycast.fromCamera(flag, 4, 20)
distance = #(playerCoords - endCoords)
if entityHit ~= 0 and entityHit ~= lastEntity then
local success, result = pcall(GetEntityType, entityHit)
entityType = success and result or 0
end
if entityType == 0 then
local _flag = flag == 511 and 26 or 511
local _hit, _entityHit, _endCoords = lib.raycast.fromCamera(_flag, 4, 20)
local _distance = #(playerCoords - _endCoords)
if _distance < distance then
flag, hit, entityHit, endCoords, distance = _flag, _hit, _entityHit, _endCoords, _distance
if entityHit ~= 0 then
local success, result = pcall(GetEntityType, entityHit)
entityType = success and result or 0
end
end
end
nearbyZones, zonesChanged = utils.getNearbyZones(endCoords)
local entityChanged = entityHit ~= lastEntity
local newOptions = (zonesChanged or entityChanged or menuChanged) and true
if entityHit > 0 and entityChanged then
currentMenu = nil
if flag ~= 511 then
entityHit = HasEntityClearLosToEntity(entityHit, cache.ped, 7) and entityHit or 0
end
if lastEntity ~= entityHit and debug then
if lastEntity then
SetEntityDrawOutline(lastEntity, false)
end
if entityType ~= 1 then
SetEntityDrawOutline(entityHit, true)
end
end
if entityHit > 0 then
local success, result = pcall(GetEntityModel, entityHit)
entityModel = success and result
end
end
if hasTarget and (zonesChanged or entityChanged and hasTarget > 1) then
SendNuiMessage('{"event": "leftTarget"}')
if entityChanged then options:wipe() end
if debug and lastEntity > 0 then SetEntityDrawOutline(lastEntity, false) end
hasTarget = false
end
if newOptions and entityModel and entityHit > 0 then
options:set(entityHit, entityType, entityModel)
end
lastEntity = entityHit
currentTarget.entity = entityHit
currentTarget.coords = endCoords
currentTarget.distance = distance
local hidden = 0
local totalOptions = 0
for k, v in pairs(options) do
local optionCount = #v
local dist = k == '__global' and 0 or distance
totalOptions += optionCount
for i = 1, optionCount do
local option = v[i]
local hide = shouldHide(option, dist, endCoords, entityHit, entityType, entityModel)
if option.hide ~= hide then
option.hide = hide
newOptions = true
end
if hide then hidden += 1 end
end
end
if zonesChanged then table.wipe(zones) end
for i = 1, #nearbyZones do
local zoneOptions = nearbyZones[i].options
local optionCount = #zoneOptions
totalOptions += optionCount
zones[i] = zoneOptions
for j = 1, optionCount do
local option = zoneOptions[j]
local hide = shouldHide(option, distance, endCoords, entityHit)
if option.hide ~= hide then
option.hide = hide
newOptions = true
end
if hide then hidden += 1 end
end
end
if newOptions then
if hasTarget == 1 and (totalOptions - hidden) > 1 then
hasTarget = true
end
if hasTarget and hidden == totalOptions then
if hasTarget and hasTarget ~= 1 then
hasTarget = false
SendNuiMessage('{"event": "leftTarget"}')
end
elseif menuChanged or hasTarget ~= 1 and hidden ~= totalOptions then
hasTarget = options.size
if currentMenu and options.__global[1]?.name ~= 'builtin:goback' then
table.insert(options.__global, 1,
{
icon = 'fa-solid fa-circle-chevron-left',
label = locale('go_back'),
name = 'builtin:goback',
menuName = currentMenu,
openMenu = 'home'
})
end
SendNuiMessage(json.encode({
event = 'setTarget',
options = options,
zones = zones,
}, { sort_keys = true }))
end
menuChanged = false
end
if toggleHotkey and IsPauseMenuActive() then
state.setActive(false)
end
if not hasTarget or hasTarget == 1 then
flag = flag == 511 and 26 or 511
end
Wait(hit and 50 or 100)
end
if lastEntity and debug then
SetEntityDrawOutline(lastEntity, false)
end
state.setNuiFocus(false)
SendNuiMessage('{"event": "visible", "state": false}')
table.wipe(currentTarget)
options:wipe()
if nearbyZones then table.wipe(nearbyZones) end
end
do
---@type KeybindProps
local keybind = {
name = 'ox_target',
defaultKey = GetConvar('ox_target:defaultHotkey', 'LMENU'),
defaultMapper = 'keyboard',
description = locale('toggle_targeting'),
}
if toggleHotkey then
function keybind:onPressed()
if state.isActive() then
return state.setActive(false)
end
return startTargeting()
end
else
keybind.onPressed = startTargeting
function keybind:onReleased()
state.setActive(false)
end
end
lib.addKeybind(keybind)
end
---@generic T
---@param option T
---@param server? boolean
---@return T
local function getResponse(option, server)
local response = table.clone(option)
response.entity = currentTarget.entity
response.zone = currentTarget.zone
response.coords = currentTarget.coords
response.distance = currentTarget.distance
if server then
response.entity = response.entity ~= 0 and NetworkGetEntityIsNetworked(response.entity) and
NetworkGetNetworkIdFromEntity(response.entity) or 0
end
response.icon = nil
response.groups = nil
response.items = nil
response.canInteract = nil
response.onSelect = nil
response.export = nil
response.event = nil
response.serverEvent = nil
response.command = nil
return response
end
RegisterNUICallback('select', function(data, cb)
cb(1)
local zone = data[3] and nearbyZones[data[3]]
---@type OxTargetOption?
local option = zone and zone.options[data[2]] or options[data[1]][data[2]]
if option then
if option.openMenu then
local menuDepth = #menuHistory
if option.name == 'builtin:goback' then
option.menuName = option.openMenu
option.openMenu = menuHistory[menuDepth]
if menuDepth > 0 then
menuHistory[menuDepth] = nil
end
else
menuHistory[menuDepth + 1] = currentMenu
end
menuChanged = true
currentMenu = option.openMenu ~= 'home' and option.openMenu or nil
options:wipe()
else
state.setNuiFocus(false)
end
currentTarget.zone = zone?.id
if option.onSelect then
option.onSelect(option.qtarget and currentTarget.entity or getResponse(option))
elseif option.export then
exports[option.resource or zone.resource][option.export](nil, getResponse(option))
elseif option.event then
TriggerEvent(option.event, getResponse(option))
elseif option.serverEvent then
TriggerServerEvent(option.serverEvent, getResponse(option, true))
elseif option.command then
ExecuteCommand(option.command)
end
if option.menuName == 'home' then return end
end
if not option?.openMenu and IsNuiFocused() then
state.setActive(false)
end
end)
================================================
FILE: client/state.lua
================================================
local state = {}
local isActive = false
---@return boolean
function state.isActive()
return isActive
end
---@param value boolean
function state.setActive(value)
isActive = value
if value then
SendNuiMessage('{"event": "visible", "state": true}')
end
end
local nuiFocus = false
---@return boolean
function state.isNuiFocused()
return nuiFocus
end
---@param value boolean
function state.setNuiFocus(value, cursor)
if value then SetCursorLocation(0.5, 0.5) end
nuiFocus = value
SetNuiFocus(value, cursor or false)
SetNuiFocusKeepInput(value)
end
local isDisabled = false
---@return boolean
function state.isDisabled()
return isDisabled
end
---@param value boolean
function state.setDisabled(value)
isDisabled = value
end
return state
================================================
FILE: client/utils.lua
================================================
local utils = {}
local GetWorldCoordFromScreenCoord = GetWorldCoordFromScreenCoord
local StartShapeTestLosProbe = StartShapeTestLosProbe
local GetShapeTestResultIncludingMaterial = GetShapeTestResultIncludingMaterial
---@param flag number
---@return boolean hit
---@return number entityHit
---@return vector3 endCoords
---@return vector3 surfaceNormal
---@return number materialHash
function utils.raycastFromCamera(flag)
local coords, normal = GetWorldCoordFromScreenCoord(0.5, 0.5)
local destination = coords + normal * 10
local handle = StartShapeTestLosProbe(coords.x, coords.y, coords.z, destination.x, destination.y, destination.z,
flag, cache.ped, 4)
while true do
Wait(0)
local retval, hit, endCoords, surfaceNormal, materialHash, entityHit = GetShapeTestResultIncludingMaterial(
handle)
if retval ~= 1 then
---@diagnostic disable-next-line: return-type-mismatch
return hit, entityHit, endCoords, surfaceNormal, materialHash
end
end
end
function utils.getTexture()
return lib.requestStreamedTextureDict('shared'), 'emptydot_32'
end
-- SetDrawOrigin is limited to 32 calls per frame. Set as 0 to disable.
local drawZoneSprites = GetConvarInt('ox_target:drawSprite', 24)
local SetDrawOrigin = SetDrawOrigin
local DrawSprite = DrawSprite
local ClearDrawOrigin = ClearDrawOrigin
local colour = vector(155, 155, 155, 175)
local hover = vector(98, 135, 236, 255)
local currentZones = {}
local previousZones = {}
local drawZones = {}
local drawN = 0
local width = 0.02
local height = width * GetAspectRatio(false)
if drawZoneSprites == 0 then drawZoneSprites = -1 end
---@param coords vector3
---@return CZone[], boolean
function utils.getNearbyZones(coords)
if not Zones then return currentZones, false end
local n = 0
local nearbyZones = lib.zones.getNearbyZones()
drawN = 0
previousZones, currentZones = currentZones, table.wipe(previousZones)
for i = 1, #nearbyZones do
local zone = nearbyZones[i]
local contains = zone:contains(coords)
if contains then
n += 1
currentZones[n] = zone
end
if drawN <= drawZoneSprites and zone.drawSprite ~= false and (contains or (zone.distance or 7) < 7) then
drawN += 1
drawZones[drawN] = zone
zone.colour = contains and hover or nil
end
end
local previousN = #previousZones
if n ~= previousN then
return currentZones, true
end
if n > 0 then
for i = 1, n do
local zoneA = currentZones[i]
local found = false
for j = 1, previousN do
local zoneB = previousZones[j]
if zoneA == zoneB then
found = true
break
end
end
if not found then
return currentZones, true
end
end
end
return currentZones, false
end
function utils.drawZoneSprites(dict, texture)
if drawN == 0 then return end
for i = 1, drawN do
local zone = drawZones[i]
local spriteColour = zone.colour or colour
if zone.drawSprite ~= false then
SetDrawOrigin(zone.coords.x, zone.coords.y, zone.coords.z)
DrawSprite(dict, texture, 0, 0, width, height, 0, spriteColour.r, spriteColour.g, spriteColour.b,
spriteColour.a)
end
end
ClearDrawOrigin()
end
function utils.hasExport(export)
local resource, exportName = string.strsplit('.', export)
return pcall(function()
return exports[resource][exportName]
end)
end
local playerItems = {}
function utils.getItems()
return playerItems
end
---@param filter string | string[] | table<string, number>
---@param hasAny boolean?
---@return boolean
function utils.hasPlayerGotItems(filter, hasAny)
if not playerItems then return true end
local _type = type(filter)
if _type == 'string' then
return (playerItems[filter] or 0) > 0
elseif _type == 'table' then
local tabletype = table.type(filter)
if tabletype == 'hash' then
for name, amount in pairs(filter) do
local hasItem = (playerItems[name] or 0) >= amount
if hasAny then
if hasItem then return true end
elseif not hasItem then
return false
end
end
elseif tabletype == 'array' then
for i = 1, #filter do
local hasItem = (playerItems[filter[i]] or 0) > 0
if hasAny then
if hasItem then return true end
elseif not hasItem then
return false
end
end
end
end
return not hasAny
end
---stub
---@param filter string | string[] | table<string, number>
---@return boolean
function utils.hasPlayerGotGroup(filter)
return true
end
SetTimeout(0, function()
if utils.hasExport('ox_inventory.Items') then
setmetatable(playerItems, {
__index = function(self, index)
self[index] = exports.ox_inventory:Search('count', index) or 0
return self[index]
end
})
AddEventHandler('ox_inventory:itemCount', function(name, count)
playerItems[name] = count
end)
end
if utils.hasExport('ox_core.GetPlayer') then
require 'client.framework.ox'
elseif utils.hasExport('es_extended.getSharedObject') then
require 'client.framework.esx'
elseif utils.hasExport('qbx_core.HasGroup') then
require 'client.framework.qbx'
elseif utils.hasExport('ND_Core.getPlayer') then
require 'client.framework.nd'
end
end)
function utils.warn(msg)
local trace = Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString())
local _, _, src = string.strsplit('\n', trace, 4)
warn(('%s ^0%s\n'):format(msg, src:gsub(".-%(", '(')))
end
return utils
================================================
FILE: fxmanifest.lua
================================================
-- FX Information
fx_version 'cerulean'
use_experimental_fxv2_oal 'yes'
nui_callback_strict_mode 'true'
lua54 'yes'
game 'gta5'
-- Resource Information
name 'ox_target'
author 'Overextended'
version '1.18.1'
repository 'https://github.com/overextended/ox_target'
description ''
-- Manifest
ui_page 'web/index.html'
shared_scripts {
'@ox_lib/init.lua',
}
client_scripts {
'client/main.lua',
}
server_scripts {
'server/main.lua'
}
files {
'web/**',
'locales/*.json',
'client/api.lua',
'client/utils.lua',
'client/state.lua',
'client/debug.lua',
'client/defaults.lua',
'client/framework/nd.lua',
'client/framework/ox.lua',
'client/framework/esx.lua',
'client/framework/qbx.lua',
'client/compat/qtarget.lua',
}
provide 'qtarget'
dependency 'ox_lib'
================================================
FILE: locales/cs.json
================================================
{
"toggle_front_driver_door": "Přední dveře řidiče",
"toggle_front_passenger_door": "Přední dveře spolujezdce",
"toggle_rear_driver_door": "Zadní dveře řidiče",
"toggle_rear_passenger_door": "Zadní dveře spolujezdce",
"toggle_hood": "Kapota",
"toggle_trunk": "Kufr",
"debug_box": "(Debug) Box",
"debug_sphere": "(Debug) Koule",
"debug_police_car": "Policejní auto",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Vozidlo",
"debug_object": "(Debug) Objekt",
"toggle_targeting": "Výběr zaměření",
"go_back": "Zpět"
}
================================================
FILE: locales/da.json
================================================
{
"toggle_front_driver_door": "Skift forreste førerdør",
"toggle_front_passenger_door": "Skift forreste passagerdør",
"toggle_rear_driver_door": "Skift bageste førerdør",
"toggle_rear_passenger_door": "Skift bageste passagerdør",
"toggle_hood": "Skift motorhjelm",
"toggle_trunk": "Skift bagagerum",
"debug_box": "(Debug) Boks",
"debug_sphere": "(Debug) Kugle",
"debug_police_car": "Politibil",
"debug_ped": "(Debug) Person",
"debug_vehicle": "(Debug) Køretøj",
"debug_object": "(Debug) Objekt",
"debug_global": "(Debug) Global",
"toggle_targeting": "Skift målretning",
"go_back": "Gå tilbage"
}
================================================
FILE: locales/de.json
================================================
{
"toggle_front_driver_door": "Vordere Fahrertür umschalten",
"toggle_front_passenger_door": "Vordere Beifahrertür umschalten",
"toggle_rear_driver_door": "Hintere Fahrertür umschalten",
"toggle_rear_passenger_door": "Hintere Beifahrertür umschalten",
"toggle_hood": "Motorhaube umschalten",
"toggle_trunk": "Kofferraum umschalten",
"debug_box": "(Debug) Box",
"debug_sphere": "(Debug) Sphäre",
"debug_police_car": "Polizeiauto",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Fahrzeug",
"debug_object": "(Debug) Objekt",
"toggle_targeting": "Zielen umschalten"
}
================================================
FILE: locales/en.json
================================================
{
"toggle_front_driver_door": "Toggle front driver door",
"toggle_front_passenger_door": "Toggle front passenger door",
"toggle_rear_driver_door": "Toggle rear driver door",
"toggle_rear_passenger_door": "Toggle rear passenger door",
"toggle_hood": "Toggle hood",
"toggle_trunk": "Toggle trunk",
"debug_box": "(Debug) Box",
"debug_sphere": "(Debug) Sphere",
"debug_police_car": "Police car",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Vehicle",
"debug_object": "(Debug) Object",
"debug_global": "(Debug) Global",
"toggle_targeting": "Toggle targeting",
"go_back": "Go back"
}
================================================
FILE: locales/es.json
================================================
{
"toggle_front_driver_door": "Abrir/Cerrar puerta delantera del conductor",
"toggle_front_passenger_door": "Abrir/Cerrar puerta delantera del pasajero",
"toggle_rear_driver_door": "Abrir/Cerrar puerta trasera del conductor",
"toggle_rear_passenger_door": "Abrir/Cerrar puerta trasera del pasajero",
"toggle_hood": "Abrir/Cerrar capó",
"toggle_trunk": "Abrir/Cerrar maletero",
"debug_box": "(Debug) Caja",
"debug_sphere": "(Debug) Esfera",
"debug_police_car": "Coche de policía",
"debug_ped": "(Debug) Peatón",
"debug_vehicle": "(Debug) Vehículo",
"debug_object": "(Debug) Objeto",
"toggle_targeting": "Activar/Desactivar apuntado"
}
================================================
FILE: locales/et.json
================================================
{
"toggle_front_driver_door": "Ava/sulge eesuks",
"toggle_front_passenger_door": "Ava/sulge eesuks",
"toggle_rear_driver_door": "Ava/sulge tagauks",
"toggle_rear_passenger_door": "Ava/sulge tagauks",
"toggle_hood": "Ava/sulge kapott",
"toggle_trunk": "Ava/sulge pagasiruum",
"debug_box": "(Debug) Kast",
"debug_sphere": "(Debug) Kera",
"debug_police_car": "Politseiauto",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Sõiduk",
"debug_object": "(Debug) Objekt",
"toggle_targeting": "Näita kolmandat silma",
"go_back": "Mine tagasi"
}
================================================
FILE: locales/fi.json
================================================
{
"toggle_front_driver_door": "Avaa/Sulje kuljettajan etuovi",
"toggle_front_passenger_door": "Avaa/Sulje repsikan etuovi",
"toggle_rear_driver_door": "Avaa/Sulje kuljettajan takaovi",
"toggle_rear_passenger_door": "Avaa/Sulje repsikan takaovi",
"toggle_hood": "Avaa/Sulje konepelti",
"toggle_trunk": "Avaa/Sulje takakontti",
"debug_box": "(Debug) Laatikko",
"debug_sphere": "(Debug) Ympyrä",
"debug_police_car": "Poliisiauto",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Ajoneuvo",
"debug_object": "(Debug) Objekti",
"toggle_targeting": "Päällä/Pois tähtäys"
}
================================================
FILE: locales/fr.json
================================================
{
"toggle_front_driver_door": "Porte conducteur avant",
"toggle_front_passenger_door": "Porte passager avant",
"toggle_rear_driver_door": "Porte conducteur arrière",
"toggle_rear_passenger_door": "Porte passager arrière",
"toggle_hood": "Capot",
"toggle_trunk": "Coffre",
"debug_box": "(Debug) Box",
"debug_sphere": "(Debug) Sphère",
"debug_police_car": "Véhicule de police",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Véhicule",
"debug_object": "(Debug) Objet",
"toggle_targeting": "Afficher le système d'interaction",
"go_back": "Retour"
}
================================================
FILE: locales/hr.json
================================================
{
"toggle_front_driver_door": "Prednja lijeva vrata",
"toggle_front_passenger_door": "Prednja desna vrata",
"toggle_rear_driver_door": "Zadnja lijeva vrata",
"toggle_rear_passenger_door": "Zadnja desna vrata",
"toggle_hood": "Hauba",
"toggle_trunk": "Gepek",
"debug_box": "(Debug) Kocka (Box)",
"debug_sphere": "(Debug) Sfera (Sphere)",
"debug_police_car": "Policijski auto",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Auto",
"debug_object": "(Debug) Objekt",
"toggle_targeting": "Upali/Ugasi Target sistem"
}
================================================
FILE: locales/hu.json
================================================
{
"toggle_front_driver_door": "Sofőroldali ajtó nyitása/zárása",
"toggle_front_passenger_door": "Anyósülés oldali ajtó nyitása/zárása",
"toggle_rear_driver_door": "Sofőroldali hátsó ajtó nyitása/zárása",
"toggle_rear_passenger_door": "Anyósülés oldali hátsó ajtó nyitása/zárása",
"toggle_hood": "Motorháztető nyitása/zárása",
"toggle_trunk": "Csomagtartó nyitása/zárása",
"debug_box": "(Hibakeresés) Doboz",
"debug_sphere": "(Hibakeresés) Gömb",
"debug_police_car": "(Hibakeresés) Rendőrautó",
"debug_ped": "(Hibakeresés) Entitás",
"debug_vehicle": "(Hibakeresés) Jármű",
"debug_object": "(Hibakeresés) Objekt",
"toggle_targeting": "Célzó be- és kikapcsolása",
"go_back": "Vissza"
}
================================================
FILE: locales/id.json
================================================
{
"toggle_front_driver_door": "Tombol pintu pengemudi depan",
"toggle_front_passenger_door": "Tombol pintu penumpang depan",
"toggle_rear_driver_door": "Tombol pintu pengemudi belakang",
"toggle_rear_passenger_door": "Tombol pintu penumpang belakang",
"toggle_hood": "Tombol kap",
"toggle_trunk": "Tombol bagasi",
"debug_box": "(Debug) Kotak",
"debug_sphere": "(Debug) Bola",
"debug_police_car": "Mobil polisi",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Kendaraan",
"debug_object": "(Debug) Objek",
"toggle_targeting": "Tombol penargetan"
}
================================================
FILE: locales/it.json
================================================
{
"toggle_front_driver_door": "Apri/Chiudi portiera anteriore sinistra",
"toggle_front_passenger_door": "Apri/Chiudi portiera anteriore destra",
"toggle_rear_driver_door": "Apri/Chiudi portiera posteriore sinistra",
"toggle_rear_passenger_door": "Apri/Chiudi portiera posteriore destra",
"toggle_hood": "Apri/Chiudi Cofano",
"toggle_trunk": "Apri/Chiudi Bagagliaio",
"debug_box": "(Debug) Box",
"debug_sphere": "(Debug) Sphere",
"debug_police_car": "Auto Polizia",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Vehicle",
"debug_object": "(Debug) Object",
"toggle_targeting": "Apri/Chiudi targeting",
"go_back": "Indietro"
}
================================================
FILE: locales/nl.json
================================================
{
"toggle_front_driver_door": "Toggle bestuurdersdeur",
"toggle_front_passenger_door": "Toggle bijrijdersdeur",
"toggle_rear_driver_door": "Toggle achterdeur links",
"toggle_rear_passenger_door": "Toggle achterdeur rechts",
"toggle_hood": "Toggle motorkap",
"toggle_trunk": "Toggle kofferbak",
"debug_box": "(Debug) Doos",
"debug_sphere": "(Debug) Bol",
"debug_police_car": "Politie voertuig",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Voertuig",
"debug_object": "(Debug) Object",
"toggle_targeting": "Toggle richten"
}
================================================
FILE: locales/no.json
================================================
{
"toggle_front_driver_door": "Åpne/lukke førerdør",
"toggle_front_passenger_door": "Åpne/lukke passasjerdør",
"toggle_rear_driver_door": "Åpne/lukke venstre bakdør",
"toggle_rear_passenger_door": "Åpne/lukke høyre bakdør",
"toggle_hood": "Åpne/lukke panser",
"toggle_trunk": "Åpne/lukke bagasjerom",
"toggle_targeting": "Sikt",
"go_back": "Tilbake"
}
================================================
FILE: locales/pl.json
================================================
{
"toggle_front_driver_door": "Przednie drzwi kierowcy",
"toggle_front_passenger_door": "Przednie drzwi pasażera",
"toggle_rear_driver_door": "Tylne drzwi kierowcy",
"toggle_rear_passenger_door": "Tylne drzwi pasażera",
"toggle_hood": "Maska",
"toggle_trunk": "Bagażnik",
"debug_box": "(Debug) Blok",
"debug_sphere": "(Debug) Kula",
"debug_police_car": "Radiowóz",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Pojazd",
"debug_object": "(Debug) Obiekt",
"toggle_targeting": "Przełącz celowanie",
"go_back": "Wróć"
}
================================================
FILE: locales/pt-br.json
================================================
{
"toggle_front_driver_door": "Abrir a porta do motorista dianteira",
"toggle_front_passenger_door": "Abrir a porta dianteira do passageiro",
"toggle_rear_driver_door": "Abrir a porta traseira do motorista",
"toggle_rear_passenger_door": "Abrir a porta traseira do passageiro",
"toggle_hood": "Abrir o capô",
"toggle_trunk": "Abrir o porta-malas",
"debug_box": "(Debug) Caixa",
"debug_sphere": "(Debug) Esfera",
"debug_police_car": "Carro de polícia",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Veículo",
"debug_object": "(Debug) Objeto",
"toggle_targeting": "Alternar mira",
"go_back": "Voltar"
}
================================================
FILE: locales/pt.json
================================================
{
"toggle_front_driver_door": "Abrir a porta do motorista dianteira",
"toggle_front_passenger_door": "Abrir a porta dianteira do passageiro",
"toggle_rear_driver_door": "Abrir a porta traseira do motorista",
"toggle_rear_passenger_door": "Abrir a porta traseira do passageiro",
"toggle_hood": "Abrir o capô",
"toggle_trunk": "Abrir o porta-malas",
"debug_box": "(Debug) Caixa",
"debug_sphere": "(Debug) Esfera",
"debug_police_car": "Carro de polícia",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Veículo",
"debug_object": "(Debug) Objeto",
"toggle_targeting": "Alternar mira",
"go_back": "Voltar"
}
================================================
FILE: locales/ro.json
================================================
{
"toggle_front_driver_door": "Comută ușa șoferului",
"toggle_front_passenger_door": "Comută ușa pasagerului",
"toggle_rear_driver_door": "Comută ușa din spatele șoferului",
"toggle_rear_passenger_door": "Comută ușa pasagerului din spate",
"toggle_hood": "Comută capota",
"toggle_trunk": "Comută portbagajul",
"debug_box": "(Debug) Cutie",
"debug_sphere": "(Debug) Sferă",
"debug_police_car": "Mașină de poliție",
"debug_ped": "(Debug) Pedestrian",
"debug_vehicle": "(Debug) Vehicul",
"debug_object": "(Debug) Obiect",
"debug_global": "(Debug) Global",
"toggle_targeting": "Foloseste ochiul",
"go_back": "Întoarce-te"
}
================================================
FILE: locales/sl.json
================================================
{
"toggle_front_driver_door": "Odpri/Zapri leva sprednja vrata",
"toggle_front_passenger_door": "Odpri/Zapri desna sprednja vrata",
"toggle_rear_driver_door": "Odpri/Zapri leva zadnja vrata",
"toggle_rear_passenger_door": "Odpri/Zapri desna zadnja vrata",
"toggle_hood": "Odpri/Zapri pokrov motorja",
"toggle_trunk": "Odpri/Zapri prtljažnik",
"debug_box": "(Debug) Kvadrat",
"debug_sphere": "(Debug) Krog",
"debug_police_car": "Policijsko vozilo",
"debug_ped": "(Debug) Pešec",
"debug_vehicle": "(Debug) Vozilo",
"debug_object": "(Debug) Predmet",
"toggle_targeting": "Vklopi/Izklopi tretje oko"
}
================================================
FILE: locales/sv.json
================================================
{
"toggle_front_driver_door": "Öppna/stäng främre förardörr",
"toggle_front_passenger_door": "Öppna/stäng främre passagerardörr",
"toggle_rear_driver_door": "Öppna/stäng bakre förardörr",
"toggle_rear_passenger_door": "Öppna/stäng bakre passagerardörr",
"toggle_hood": "Öppna/stäng motorhuv",
"toggle_trunk": "Öppna/stäng bagagelucka",
"debug_box": "(Debug) Låda",
"debug_sphere": "(Debug) Sfär",
"debug_police_car": "Polisbil",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Fordon",
"debug_object": "(Debug) Objekt",
"debug_global": "(Debug) Global",
"toggle_targeting": "Växla Target",
"go_back": "Gå tillbaka"
}
================================================
FILE: locales/tr.json
================================================
{
"toggle_front_driver_door": "Ön sürücü tarafı kapısını aç/kapa",
"toggle_front_passenger_door": "Ön yolcu tarafı kapısını aç/kapa",
"toggle_rear_driver_door": "Arka sürücü tarafı kapısını aç/kapa",
"toggle_rear_passenger_door": "Arka yolcu tarafı kapısını aç/kapa",
"toggle_hood": "Kaputu aç/kapa",
"toggle_trunk": "Bagajı aç/kapa",
"debug_box": "(Debug) Kutu",
"debug_sphere": "(Debug) Küre",
"debug_police_car": "Polis arabası",
"debug_ped": "(Debug) Ped",
"debug_vehicle": "(Debug) Araç",
"debug_object": "(Debug) Nesne",
"debug_global": "(Debug) Genel",
"toggle_targeting": "Hedeflemeyi aç/kapa",
"go_back": "Geri dön"
}
================================================
FILE: locales/zh-cn.json
================================================
{
"toggle_front_driver_door": "开关左前车门",
"toggle_front_passenger_door": "开关左后车门",
"toggle_rear_driver_door": "开关右前车门",
"toggle_rear_passenger_door": "开关右后车门",
"toggle_hood": "打开引擎盖",
"toggle_trunk": "打开后备箱",
"debug_box": "(Debug) 矩形区域",
"debug_sphere": "(Debug) 球形区域",
"debug_police_car": "警车",
"debug_ped": "(Debug) 角色实体",
"debug_vehicle": "(Debug) 车辆",
"debug_object": "(Debug) 物体",
"debug_global": "(Debug) 全局对象",
"toggle_targeting": "交互菜单",
"go_back": "返回"
}
================================================
FILE: locales/zh-tw.json
================================================
{
"toggle_front_driver_door": "開關左前車門",
"toggle_front_passenger_door": "開關左後車門",
"toggle_rear_driver_door": "開關右前車門",
"toggle_rear_passenger_door": "開關右後車門",
"toggle_hood": "打開引擎蓋",
"toggle_trunk": "打開後備箱",
"debug_box": "(Debug) 矩形區域",
"debug_sphere": "(Debug) 球形區域",
"debug_police_car": "警車",
"debug_ped": "(Debug) 角色實體",
"debug_vehicle": "(Debug) 車輛",
"debug_object": "(Debug) 物體",
"debug_global": "(Debug) 全局對象",
"toggle_targeting": "交互菜單",
"go_back": "返回"
}
================================================
FILE: server/main.lua
================================================
lib.versionCheck('overextended/ox_target')
if not lib.checkDependency('ox_lib', '3.30.0', true) then return end
---@type table<number, EntityInterface>
local entityStates = {}
---@param netId number
RegisterNetEvent('ox_target:setEntityHasOptions', function(netId)
local entity = Entity(NetworkGetEntityFromNetworkId(netId))
entity.state.hasTargetOptions = true
entityStates[netId] = entity
end)
---@param netId number
---@param door number
RegisterNetEvent('ox_target:toggleEntityDoor', function(netId, door)
local entity = NetworkGetEntityFromNetworkId(netId)
if not DoesEntityExist(entity) then return end
local owner = NetworkGetEntityOwner(entity)
TriggerClientEvent('ox_target:toggleEntityDoor', owner, netId, door)
end)
CreateThread(function()
local arr = {}
local num = 0
while true do
Wait(10000)
for netId, entity in pairs(entityStates) do
if not DoesEntityExist(entity.__data) or not entity.state.hasTargetOptions then
entityStates[netId] = nil
num += 1
arr[num] = netId
end
end
if num > 0 then
TriggerClientEvent('ox_target:removeEntity', -1, arr)
table.wipe(arr)
num = 0
end
end
end)
================================================
FILE: web/index.html
================================================
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" />
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="eye">
<svg id="eyeSvg" xmlns="http://www.w3.org/2000/svg" height="36px" viewBox="0 0 24 24" width="36px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 4C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
</div>
<div id="options-wrapper"></div>
<script defer type="module" src="js/main.js"></script>
</body>
</html>
================================================
FILE: web/js/createOptions.js
================================================
import { fetchNui } from "./fetchNui.js";
const optionsWrapper = document.getElementById("options-wrapper");
function onClick() {
// when nuifocus is disabled after a click, the hover event is never released
this.style.pointerEvents = "none";
fetchNui("select", [this.targetType, this.targetId, this.zoneId]);
// is there a better way to handle this? probably
setTimeout(() => (this.style.pointerEvents = "auto"), 100);
}
export function createOptions(type, data, id, zoneId) {
if (data.hide) return;
const option = document.createElement("div");
const iconElement = `<i class="fa-fw ${data.icon} option-icon" ${
data.iconColor ? `style = color:${data.iconColor} !important` : null
}"></i>`;
option.innerHTML = `${iconElement}<p class="option-label">${data.label}</p>`;
option.className = "option-container";
option.targetType = type;
option.targetId = id;
option.zoneId = zoneId;
option.addEventListener("click", onClick);
optionsWrapper.appendChild(option);
}
================================================
FILE: web/js/fetchNui.js
================================================
const resource = GetParentResourceName();
export async function fetchNui(eventName, data) {
const resp = await fetch(`https://${resource}/${eventName}`, {
method: 'post',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: JSON.stringify(data),
});
return await resp.json();
}
================================================
FILE: web/js/main.js
================================================
import { createOptions } from "./createOptions.js";
const optionsWrapper = document.getElementById("options-wrapper");
const body = document.body;
const eye = document.getElementById("eyeSvg");
window.addEventListener("message", (event) => {
switch (event.data.event) {
case "visible": {
optionsWrapper.innerHTML = "";
body.style.visibility = event.data.state ? "visible" : "hidden";
return eye.classList.remove("eye-hover");
}
case "leftTarget": {
optionsWrapper.innerHTML = "";
return eye.classList.remove("eye-hover");
}
case "setTarget": {
optionsWrapper.innerHTML = "";
eye.classList.add("eye-hover");
if (event.data.options) {
for (const type in event.data.options) {
event.data.options[type].forEach((data, id) => {
createOptions(type, data, id + 1);
});
}
}
if (event.data.zones) {
for (let i = 0; i < event.data.zones.length; i++) {
event.data.zones[i].forEach((data, id) => {
createOptions("zones", data, id + 1, i + 1);
});
}
}
}
}
});
================================================
FILE: web/style.css
================================================
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap");
:root {
--color-default: #cfd2da;
--color-hover: white;
}
body {
visibility: hidden;
user-select: none;
white-space: nowrap;
margin: 0;
user-select: none;
overflow: hidden;
}
p {
margin: 0;
}
.material-symbols-outlined {
font-variation-settings: "FILL" 0, "wght" 300, "GRAD" 0, "opsz" 40;
}
#eye {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 22pt;
fill: black;
}
.eye-hover {
fill: var(--color-default);
}
#options-wrapper {
position: absolute;
top: calc(48.4%);
left: calc(50% + 18pt);
}
.option-container {
color: var(--color-default);
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
font-family: "Nunito";
background: linear-gradient(
90deg,
rgba(20, 20, 20, 0.7) 0%,
rgba(20, 20, 20, 0.6) 66%,
rgba(47, 48, 53, 0) 100%
);
font-size: 11pt;
line-height: 22pt;
vertical-align: middle;
margin: 2pt;
transition: 300ms;
transform-origin: left top;
scale: 1;
height: 22pt;
width: 150pt;
top: 0;
}
.option-container:hover {
background: linear-gradient(
90deg,
rgba(30, 30, 30, 0.7) 0%,
rgba(30, 30, 30, 0.6) 66%,
rgba(57, 58, 63, 0) 100%
);
transform-origin: left top;
color: var(--color-hover);
margin-left: 4pt;
}
.option-icon {
font-size: 12pt;
line-height: 22pt;
width: 14pt;
margin: 5pt;
color: var(--color-default);
}
.option-label {
font-weight: 500;
}
gitextract_ba2qc7hr/
├── .github/
│ ├── actions/
│ │ └── bump-manifest-version.js
│ └── workflows/
│ └── release.yml
├── LICENSE
├── README.md
├── client/
│ ├── api.lua
│ ├── compat/
│ │ └── qtarget.lua
│ ├── debug.lua
│ ├── defaults.lua
│ ├── framework/
│ │ ├── esx.lua
│ │ ├── nd.lua
│ │ ├── ox.lua
│ │ └── qbx.lua
│ ├── main.lua
│ ├── state.lua
│ └── utils.lua
├── fxmanifest.lua
├── locales/
│ ├── cs.json
│ ├── da.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── et.json
│ ├── fi.json
│ ├── fr.json
│ ├── hr.json
│ ├── hu.json
│ ├── id.json
│ ├── it.json
│ ├── nl.json
│ ├── no.json
│ ├── pl.json
│ ├── pt-br.json
│ ├── pt.json
│ ├── ro.json
│ ├── sl.json
│ ├── sv.json
│ ├── tr.json
│ ├── zh-cn.json
│ └── zh-tw.json
├── server/
│ └── main.lua
└── web/
├── index.html
├── js/
│ ├── createOptions.js
│ ├── fetchNui.js
│ └── main.js
└── style.css
SYMBOL INDEX (3 symbols across 2 files)
FILE: web/js/createOptions.js
function onClick (line 5) | function onClick() {
function createOptions (line 14) | function createOptions(type, data, id, zoneId) {
FILE: web/js/fetchNui.js
function fetchNui (line 3) | async function fetchNui(eventName, data) {
Condensed preview — 45 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (83K chars).
[
{
"path": ".github/actions/bump-manifest-version.js",
"chars": 302,
"preview": "const fxManifest = await Bun.file('./fxmanifest.lua').text();\n\nlet newVersion = process.env.TGT_RELEASE_VERSION;\nnewVers"
},
{
"path": ".github/workflows/release.yml",
"chars": 1771,
"preview": "name: Create release\n\non:\n push:\n tags:\n - \"v*.*.*\"\n\njobs:\n create-release:\n if: github.actor_id != 2789033"
},
{
"path": "LICENSE",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2022 Overextended\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "README.md",
"chars": 1291,
"preview": "# ox_target\n\n\n\n AddEventHandler(('__cfx_export_qtarget_%s'):format(exportName), funct"
},
{
"path": "client/debug.lua",
"chars": 2255,
"preview": "AddEventHandler('ox_target:debug', function(data)\n if data.entity and GetEntityType(data.entity) > 0 then\n dat"
},
{
"path": "client/defaults.lua",
"chars": 4419,
"preview": "if GetConvarInt('ox_target:defaults', 1) ~= 1 then return end\n\nlocal api = require 'client.api'\nlocal GetEntityBoneIndex"
},
{
"path": "client/framework/esx.lua",
"chars": 2428,
"preview": "local ESX = exports.es_extended:getSharedObject()\nlocal utils = require 'client.utils'\nlocal groups = { 'job', 'job2' }\n"
},
{
"path": "client/framework/nd.lua",
"chars": 1251,
"preview": "local NDCore = exports[\"ND_Core\"]\n\nlocal playerGroups = NDCore:getPlayer()?.groups or {}\n\nRegisterNetEvent(\"ND:character"
},
{
"path": "client/framework/ox.lua",
"chars": 331,
"preview": "if not lib.checkDependency('ox_core', '0.21.3', true) then return end\n\nlocal Ox = require '@ox_core.lib.init' --[[@as Ox"
},
{
"path": "client/framework/qbx.lua",
"chars": 270,
"preview": "if not lib.checkDependency('qbx_core', '1.18.0', true) then return end\n\nlocal QBX = exports.qbx_core\nlocal utils = requi"
},
{
"path": "client/main.lua",
"chars": 14302,
"preview": "if not lib.checkDependency('ox_lib', '3.30.0', true) then return end\n\nlib.locale()\n\nlocal utils = require 'client.utils'"
},
{
"path": "client/state.lua",
"chars": 795,
"preview": "local state = {}\n\nlocal isActive = false\n\n---@return boolean\nfunction state.isActive()\n return isActive\nend\n\n---@para"
},
{
"path": "client/utils.lua",
"chars": 6109,
"preview": "local utils = {}\n\nlocal GetWorldCoordFromScreenCoord = GetWorldCoordFromScreenCoord\nlocal StartShapeTestLosProbe = Start"
},
{
"path": "fxmanifest.lua",
"chars": 813,
"preview": "-- FX Information\nfx_version 'cerulean'\nuse_experimental_fxv2_oal 'yes'\nnui_callback_strict_mode 'true'\nlua54 'yes'\ngame"
},
{
"path": "locales/cs.json",
"chars": 550,
"preview": "{\n \"toggle_front_driver_door\": \"Přední dveře řidiče\",\n \"toggle_front_passenger_door\": \"Přední dveře spolujezdce\",\n \"t"
},
{
"path": "locales/da.json",
"chars": 626,
"preview": "{\n \"toggle_front_driver_door\": \"Skift forreste førerdør\",\n \"toggle_front_passenger_door\": \"Skift forreste passagerdør\""
},
{
"path": "locales/de.json",
"chars": 612,
"preview": "{\r\n \"toggle_front_driver_door\": \"Vordere Fahrertür umschalten\",\r\n \"toggle_front_passenger_door\": \"Vordere Beifahrertür"
},
{
"path": "locales/en.json",
"chars": 634,
"preview": "{\r\n \"toggle_front_driver_door\": \"Toggle front driver door\",\r\n \"toggle_front_passenger_door\": \"Toggle front passenger d"
},
{
"path": "locales/es.json",
"chars": 660,
"preview": "{\n \"toggle_front_driver_door\": \"Abrir/Cerrar puerta delantera del conductor\",\n \"toggle_front_passenger_door\": \"Abrir/C"
},
{
"path": "locales/et.json",
"chars": 568,
"preview": "{\n \"toggle_front_driver_door\": \"Ava/sulge eesuks\",\n \"toggle_front_passenger_door\": \"Ava/sulge eesuks\",\n \"toggle_rear_"
},
{
"path": "locales/fi.json",
"chars": 598,
"preview": "{\n \"toggle_front_driver_door\": \"Avaa/Sulje kuljettajan etuovi\",\n \"toggle_front_passenger_door\": \"Avaa/Sulje repsikan e"
},
{
"path": "locales/fr.json",
"chars": 581,
"preview": "{\n \"toggle_front_driver_door\": \"Porte conducteur avant\",\n \"toggle_front_passenger_door\": \"Porte passager avant\",\n \"to"
},
{
"path": "locales/hr.json",
"chars": 547,
"preview": "{\n \"toggle_front_driver_door\": \"Prednja lijeva vrata\",\n \"toggle_front_passenger_door\": \"Prednja desna vrata\",\n \"toggl"
},
{
"path": "locales/hu.json",
"chars": 699,
"preview": "{\r\n\"toggle_front_driver_door\": \"Sofőroldali ajtó nyitása/zárása\",\r\n\"toggle_front_passenger_door\": \"Anyósülés oldali ajtó"
},
{
"path": "locales/id.json",
"chars": 594,
"preview": "{\r\n \"toggle_front_driver_door\": \"Tombol pintu pengemudi depan\",\r\n \"toggle_front_passenger_door\": \"Tombol pintu penumpa"
},
{
"path": "locales/it.json",
"chars": 660,
"preview": "{\n \"toggle_front_driver_door\": \"Apri/Chiudi portiera anteriore sinistra\",\n \"toggle_front_passenger_door\": \"Apri/Chiudi"
},
{
"path": "locales/nl.json",
"chars": 558,
"preview": "{\n \"toggle_front_driver_door\": \"Toggle bestuurdersdeur\",\n \"toggle_front_passenger_door\": \"Toggle bijrijdersdeur\",\n \"t"
},
{
"path": "locales/no.json",
"chars": 368,
"preview": "{\n \"toggle_front_driver_door\": \"Åpne/lukke førerdør\",\n \"toggle_front_passenger_door\": \"Åpne/lukke passasjerdør\",\n \"to"
},
{
"path": "locales/pl.json",
"chars": 552,
"preview": "{\n \"toggle_front_driver_door\": \"Przednie drzwi kierowcy\",\n \"toggle_front_passenger_door\": \"Przednie drzwi pasażera\",\n "
},
{
"path": "locales/pt-br.json",
"chars": 637,
"preview": "{\n \"toggle_front_driver_door\": \"Abrir a porta do motorista dianteira\",\n \"toggle_front_passenger_door\": \"Abrir a porta "
},
{
"path": "locales/pt.json",
"chars": 637,
"preview": "{\n \"toggle_front_driver_door\": \"Abrir a porta do motorista dianteira\",\n \"toggle_front_passenger_door\": \"Abrir a porta "
},
{
"path": "locales/ro.json",
"chars": 650,
"preview": "{\n \"toggle_front_driver_door\": \"Comută ușa șoferului\",\n \"toggle_front_passenger_door\": \"Comută ușa pasagerului\",\n \"to"
},
{
"path": "locales/sl.json",
"chars": 626,
"preview": "{\n \"toggle_front_driver_door\": \"Odpri/Zapri leva sprednja vrata\",\n \"toggle_front_passenger_door\": \"Odpri/Zapri desna s"
},
{
"path": "locales/sv.json",
"chars": 653,
"preview": "{\n \"toggle_front_driver_door\": \"Öppna/stäng främre förardörr\",\n \"toggle_front_passenger_door\": \"Öppna/stäng främre pas"
},
{
"path": "locales/tr.json",
"chars": 657,
"preview": "{\n \"toggle_front_driver_door\": \"Ön sürücü tarafı kapısını aç/kapa\",\n \"toggle_front_passenger_door\": \"Ön yolcu tarafı k"
},
{
"path": "locales/zh-cn.json",
"chars": 492,
"preview": "{\n \"toggle_front_driver_door\": \"开关左前车门\",\n \"toggle_front_passenger_door\": \"开关左后车门\",\n \"toggle_rear_driver_door\": \"开关右前车"
},
{
"path": "locales/zh-tw.json",
"chars": 509,
"preview": "{\r\n \"toggle_front_driver_door\": \"開關左前車門\",\r\n \"toggle_front_passenger_door\": \"開關左後車門\",\r\n \"toggle_rear_driver_door\": \"開關"
},
{
"path": "server/main.lua",
"chars": 1300,
"preview": "lib.versionCheck('overextended/ox_target')\n\nif not lib.checkDependency('ox_lib', '3.30.0', true) then return end\n\n---@ty"
},
{
"path": "web/index.html",
"chars": 736,
"preview": "<html>\n <head>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min."
},
{
"path": "web/js/createOptions.js",
"chars": 1005,
"preview": "import { fetchNui } from \"./fetchNui.js\";\n\nconst optionsWrapper = document.getElementById(\"options-wrapper\");\n\nfunction "
},
{
"path": "web/js/fetchNui.js",
"chars": 326,
"preview": "const resource = GetParentResourceName();\n\nexport async function fetchNui(eventName, data) {\n const resp = await fetch("
},
{
"path": "web/js/main.js",
"chars": 1143,
"preview": "import { createOptions } from \"./createOptions.js\";\n\nconst optionsWrapper = document.getElementById(\"options-wrapper\");\n"
},
{
"path": "web/style.css",
"chars": 1579,
"preview": "@import url(\"https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap\");\n\n:root {\n --color"
}
]
About this extraction
This page contains the full source code of the overextended/ox_target GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 45 files (74.4 KB), approximately 20.7k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.