Client-side interaction API for registering world/entity targets that render the DUI HUD and execute actions.
local interact = exports.dc_interactaddCoords(coords, options)
local id = interact:addCoords(vec3(100.0, 200.0, 30.0), {
{ id = 'open', key = 'E', label = 'Open', icon = 'box', onSelect = function(ctx) print(ctx.entity) end }
})| Parameters | coords — vector3 or { x, y, z }; options — InteractOption or InteractOption[] |
| Returns | id — string (target id for removal) |
removeCoords(id)
Remove target by id (same function is used for all target types).
| Parameters | id — string |
| Returns | true if removed, false if id not found |
addEntity(netId, options)
Register interaction on a networked entity.
| Parameters | netId — number; options — InteractOption or InteractOption[] |
| Returns | id — string when successful; nil when netId is invalid or entity unavailable |
removeEntity(id)
Alias of removeCoords.
| Parameters | id — string |
| Returns | boolean |
addLocalEntity(entity, options)
Register interaction on a local entity handle.
Also supports a wrapper shape:
{
bone = "boot",
offset = vec3(0.0, -1.2, 0.5),
options = { ... }
}| Parameters | entity — number (entity handle); options — InteractOption or InteractOption[], or wrapper with bone, offset, options |
| Returns | id — string when successful; nil when entity does not exist |
removeLocalEntity(id)
Alias of removeCoords.
| Parameters | id — string |
| Returns | boolean |
addGlobalPlayer(options)
Register interaction options for all targeted player peds.
| Parameters | options — InteractOption or InteractOption[] |
| Returns | id — string (global target id for removal) |
removeGlobalPlayer(id)
Removes a previously added global player target.
| Parameters | id — string |
| Returns | boolean |
addGlobalVehicle(options)
Register interaction options for all targeted vehicles.
| Parameters | options — InteractOption or InteractOption[] |
| Returns | id — string (global target id for removal) |
removeGlobalVehicle(id)
Removes a previously added global vehicle target.
| Parameters | id — string |
| Returns | boolean |
setInteractionTheme(color)
Send UI theme color to DUI.
| Parameters | color — { number, number, number, number } (RGBA 0–255) |
| Returns | true when sent; false if DUI not ready or color payload invalid |
InteractOption
---@class InteractOption
---@field id? string -- stable id (recommended)
---@field name? string -- fallback id source
---@field key? string -- default: "E"
---@field label string -- required display text
---@field icon? string -- FA short name or full classes
---@field holdMs? number -- hold duration in ms (0 = tap)
---@field distance? number -- action distance, default: config.interactDistance
---@field bone? string -- single entity bone name
---@field bones? string|string[] -- one or multiple entity bone names
---@field group? string|InteractGroupFilter
---@field owner? number -- player dataId that can see/use this option
---@field canInteract? fun(ctx: InteractFilterContext): boolean
---@field onSelect? fun(ctx: InteractContext)
---@field event? string -- TriggerEvent(event, ctx)
---@field serverEvent? string -- TriggerServerEvent(serverEvent, ctx)
---@field command? string -- ExecuteCommand(command)
---@field export? string -- "resource:exportName"
---@field resource? string -- auto-set from invoking resourceVisibility filters
Options are only shown when all filters pass.
---@class InteractGroupFilter
---@field name string
---@field grade? number|number[] -- grade index (example: 1, 2, 3...)group = "police": player stategroup.namemust match.group = { name = "police", grade = 3 }: name + minimum grade index (playerGrade >= 3).group = { name = "police", grade = { 2, 3, 4 } }: name + any listed grade index (exact match against the list).owner = 12345: only player withLocalPlayer.state.dataId == 12345.canInteract = function(ctx) ... end: dynamic per-frame check; hide when it returnsfalseor errors.
group and owner are synced live via state bag changes (group, dataId), so visibility updates immediately after state changes.
Bone targeting
bone = "door_pside_f": anchor to one bone.bones = "door_pside_f": same asbone.bones = { "door_pside_f", "door_dside_f" }: first valid bone is used.- When a bone is set, distance and
canInteractcontext are evaluated from that bone position.
InteractFilterContext
Passed to canInteract:
---@class InteractFilterContext
---@field target table -- internal target entry
---@field entity number|nil
---@field coords vector3|nil
---@field distanceSq number
---@field distance numberAction execution order
An option can trigger these, in this exact order:
onSelectexporteventserverEventcommand
InteractContext
Passed to onSelect, and forwarded for event / serverEvent:
---@class InteractContext
---@field entity number|nil
---@field coords vector3|nil
---@field distance number|nilExample
local id = exports.dc_interact:addLocalEntity(vehicle, {
offset = vec3(0.0, -1.4, 0.55),
options = {
{
id = 'open_trunk',
key = 'E',
label = 'Open Trunk',
icon = 'box',
holdMs = 2000,
distance = 2.5,
group = { name = 'police', grade = { 2, 3, 4 } },
owner = 1203, -- dataId
canInteract = function(ctx)
if not ctx.entity then return false end
return GetVehicleDoorLockStatus(ctx.entity) ~= 2
end,
onSelect = function(ctx)
print('Selected on entity', ctx.entity)
end,
},
},
})
exports.dc_interact:removeLocalEntity(id)Global vehicle example
local globalId = exports.dc_interact:addGlobalVehicle({
{
id = 'toggle_front_passenger',
key = 'E',
label = 'Toggle front passenger door',
icon = 'car-side',
bones = 'door_pside_f',
distance = 2.0,
canInteract = function(ctx)
return ctx.entity and GetVehicleDoorLockStatus(ctx.entity) ~= 2
end,
onSelect = function(ctx)
if not ctx.entity then return end
if GetVehicleDoorAngleRatio(ctx.entity, 1) > 0.0 then
SetVehicleDoorShut(ctx.entity, 1, false)
else
SetVehicleDoorOpen(ctx.entity, 1, false, false)
end
end,
}
})
exports.dc_interact:removeGlobalVehicle(globalId)