Skip to content

lumo-interact ​

Optimized interaction engine for coordinates, entities, and models. It replaces scattered DrawText / [E] press loops with one stable export API.

Detection and input remain in this resource. All visuals are rendered by the React/TypeScript lumo-ui overlay, so interactions automatically inherit the canonical accent, type, scale, prompt position, and reduced-motion setting.

Supports coords, a specific entity, or every entity of a model, with multiple options per target, quick-press OR hold, per-option permission gates, and firing client actions, client events, server events, or commands. The prompt only appears when you walk into range, and the engine idles cheaply when nothing is nearby.

Ships with Lumo UI. Start it after lumo-ui:

cfg
ensure lumo-interact

Exports ​

lua
local id = exports['lumo-interact']:addCoords(id, coords, data)  -- id optional
local id = exports['lumo-interact']:addEntity(entity, data)
local id = exports['lumo-interact']:addModel(models, data)       -- string|hash|table
exports['lumo-interact']:remove(id)
exports['lumo-interact']:removeEntity(entity)
exports['lumo-interact']:has(id)                                 -- still registered?

-- suspend every prompt (cutscene, minigame, your own menu); each resource
-- holds its own flag, so two callers cannot undo each other
exports['lumo-interact']:setDisabled(true)
exports['lumo-interact']:setDisabled(false)

exports['lumo-interact']:IsFocused()   -- a prompt card is on screen
exports['lumo-interact']:getFocus()    -- { id, kind, entity, distance } or nil

-- simple textui passthrough
exports['lumo-interact']:showText('Hold to search', 'G')
exports['lumo-interact']:hideText()

data shape ​

lua
{
  distance = 2.5,                       -- reveal distance (default 2.0)
  offset   = vector3(0.0, 0.0, 0.0),    -- offset prompt/marker from target
  anchor   = 'origin',                   -- optional: use entity origin instead of its centre/head
  options  = {
    {
      label = 'Talk to Officer',
      key   = 'E',                      -- name from Config.Keys (E,G,F,H,R,X,...)
      hold  = 0,                        -- ms to hold; 0 = quick press; unset = Config.DefaultHold
      canInteract = function(entity, dist, id) return true end,
      action      = function(entity, coords, id) print('pressed') end,  -- or onSelect
      event       = 'my:client:event',  args = { 1, 'foo' },
      serverEvent = 'my:server:event',  serverArgs = { 'bar' },
      command     = 'opensomething',
    },
  },
}

Examples ​

Coordinate prompts use the exact supplied point plus offset. Object/vehicle prompts use the centre of the model bounds; peds keep the prompt above their head. Supply a waist-height point for a fixed door or counter. anchor = 'origin' opts an entity target into an explicit origin-based position.

Fixed coord (the police duty desk in your screenshot):

lua
exports['lumo-interact']:addCoords('police_duty', vector3(441.0, -982.0, 30.7), {
  distance = 1.8,
  options = {
    { label = 'Local Duty Officer', key = 'E',
      action = function() TriggerEvent('lumo-localjob:client:openDuty') end },
  },
})

A model (every dumpster), with a hold:

lua
exports['lumo-interact']:addModel({ `prop_dumpster_01a`, `prop_dumpster_02a` }, {
  distance = 2.0,
  options = {
    { label = 'Search', key = 'G', hold = 1200,
      serverEvent = 'lumo:server:searchDumpster' },
  },
})

A spawned ped entity with two options + a permission gate:

lua
local ped = -- your CreatePed handle
exports['lumo-interact']:addEntity(ped, {
  options = {
    { label = 'Shop',  key = 'E', action = function() openShop() end },
    { label = 'Rob',   key = 'H', hold = 2000,
      canInteract = function() return hasWeapon() end,
      serverEvent = 'store:server:rob' },
  },
})

Migrating an old [E] DrawText block ​

Old per-frame pattern:

lua
if dist < 2.0 then
  DrawText3D(coords, '[E] Open')
  if IsControlJustPressed(0, 38) then open() end
end

becomes one registration (no per-frame loop in your script):

lua
exports['lumo-interact']:addCoords('thing', coords, {
  options = { { label = 'Open', key = 'E', action = open } },
})

Config (config.lua) ​

  • Config.DefaultDistance, Config.DefaultHold — defaults for options that set neither. An option without hold is a hold of DefaultHold ms.
  • Config.DisableWhenDead, Config.HideWhenNuiFocused — prompts and keys are also suspended while the pause menu is open.
  • Config.Keys — add/rename keys (name → control id + glyph).
  • Config.IdleWait, Config.ScanInterval, Config.ScanRadius, Config.PositionInterval — performance tuning.
  • Config.ServerRelayEvents — exact allowlist for the optional generic relay; empty by default for security.

Architecture ​

  • lumo-interact: world scans, range gates, input, hold timing, and actions.
  • lumo-ui: floating option cards, key progress, simple prompts, and theme.
  • Registrations belong to the resource that made them and are removed when it stops. Deleted entities are dropped automatically.
  • One key press fires one option: when two allowed options share a key, the first one listed wins.
  • A callback that errors is logged and skipped; it cannot stop other prompts.
  • Existing addCoords, addEntity, addModel, showText, and removal exports remain unchanged for compatibility with every existing consumer.