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 }
})
Parameterscoords — vector3 or { x, y, z }; options — InteractOption or InteractOption[]
Returnsid — string (target id for removal)

removeCoords(id)

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

Parametersid — string
Returnstrue if removed, false if id not found

addEntity(netId, options)

Register interaction on a networked entity.

ParametersnetId — number; options — InteractOption or InteractOption[]
Returnsid — string when successful; nil when netId is invalid or entity unavailable

removeEntity(id)

Alias of removeCoords.

Parametersid — string
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 = { ... }
}
Parametersentity — number (entity handle); options — InteractOption or InteractOption[], or wrapper with bone, offset, options
Returnsid — string when successful; nil when entity does not exist

removeLocalEntity(id)

Alias of removeCoords.

Parametersid — string
Returnsboolean

addGlobalPlayer(options)

Register interaction options for all targeted player peds.

Parametersoptions — InteractOption or InteractOption[]
Returnsid — string (global target id for removal)

removeGlobalPlayer(id)

Removes a previously added global player target.

Parametersid — string
Returnsboolean

addGlobalVehicle(options)

Register interaction options for all targeted vehicles.

Parametersoptions — InteractOption or InteractOption[]
Returnsid — string (global target id for removal)

removeGlobalVehicle(id)

Removes a previously added global vehicle target.

Parametersid — string
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