Downtown Chronicles
Interface

Interact

dc_interact — client DUI HUD for world/entity targets and actions.

Client-side interaction API for registering world/entity targets that render the DUI HUD and execute actions.

local interact = exports.dc_interact

addCoords(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 }
})
Parameterscoordsvector3 or { x, y, z }; optionsInteractOption or InteractOption[]
Returnsidstring (target id for removal)

removeCoords(id)

Remove target by id (same function is used for all target types).

Parametersidstring
Returnstrue if removed, false if id not found

addEntity(netId, options)

Register interaction on a networked entity.

ParametersnetIdnumber; optionsInteractOption or InteractOption[]
Returnsidstring when successful; nil when netId is invalid or entity unavailable

removeEntity(id)

Alias of removeCoords.

Parametersidstring
Returnsboolean

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 = { ... }
}
Parametersentitynumber (entity handle); optionsInteractOption or InteractOption[], or wrapper with bone, offset, options
Returnsidstring when successful; nil when entity does not exist

removeLocalEntity(id)

Alias of removeCoords.

Parametersidstring
Returnsboolean

addGlobalPlayer(options)

Register interaction options for all targeted player peds.

ParametersoptionsInteractOption or InteractOption[]
Returnsidstring (global target id for removal)

removeGlobalPlayer(id)

Removes a previously added global player target.

Parametersidstring
Returnsboolean

addGlobalVehicle(options)

Register interaction options for all targeted vehicles.

ParametersoptionsInteractOption or InteractOption[]
Returnsidstring (global target id for removal)

removeGlobalVehicle(id)

Removes a previously added global vehicle target.

Parametersidstring
Returnsboolean

setInteractionTheme(color)

Send UI theme color to DUI.

Parameterscolor{ number, number, number, number } (RGBA 0–255)
Returnstrue 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 resource

Visibility 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 state group.name must 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 with LocalPlayer.state.dataId == 12345.
  • canInteract = function(ctx) ... end: dynamic per-frame check; hide when it returns false or 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 as bone.
  • bones = { "door_pside_f", "door_dside_f" }: first valid bone is used.
  • When a bone is set, distance and canInteract context 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 number

Action execution order

An option can trigger these, in this exact order:

  1. onSelect
  2. export
  3. event
  4. serverEvent
  5. command

InteractContext

Passed to onSelect, and forwarded for event / serverEvent:

---@class InteractContext
---@field entity number|nil
---@field coords vector3|nil
---@field distance number|nil

Example

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)

On this page