All exports are client-side only — call them from client scripts in your resource. For same-resource callbacks, use RegisterAction({...}) (global) instead of the export; Lua function closures do not survive across export boundaries.
Register a new voice action.
Syntaxexports['onex-voiceInteraction']:registerAction(def)
| Name | Type | Required | Description |
|---|---|---|---|
name | string | ✅ | Unique action identifier |
phrases | string[] | ✅ | Spoken phrases; use {var} for variable slots |
trigger | table | ✅ | What fires on match — see Trigger types |
layer | string | — | Layer to assign: "ambient", "context", "global", or custom |
group | string | — | Batch enable/disable group name |
tags | string[] | — | Filter tags for queryActions |
description | string | — | Max 200 chars; used by LLM tier matching |
minSimilarity | number | — | Match confidence threshold (0.0–1.0, default 0.7) |
condition | function | — | function(data) return boolean — pre-execution gate |
variableAliases | table | — | Synonym map { amount = "sum" } |
variableTypes | table | — | Type coercion { amount = "number" } |
variableDefaults | table | — | Fallback values { size = "large" } |
numberNormalization | boolean | — | Enable automatic word-to-number parsing (e.g. "five" → 5) |
Returns: { success = true, data = { name = "action_name" } }
Your Scriptexports['onex-voiceInteraction']:registerAction({ name = "buy_water", phrases = { "buy water", "give me water" }, trigger = { event = { name = "myres:buyItem", type = "server", params = { item = "water" } } }, group = "voice_shop_1", minSimilarity = 0.7 })
Unregister one action, all from a resource, or all from the calling resource.
Syntaxexports['onex-voiceInteraction']:unregisterAction(target)
| Name | Type | Description |
|---|---|---|
target | string | Action name, "@resource_name" for all from a resource, or omit for calling resource |
Returns: { success = true, data = { count = 1 } }
Your Scriptexports['onex-voiceInteraction']:unregisterAction("buy_water") exports['onex-voiceInteraction']:unregisterAction("@my-resource") exports['onex-voiceInteraction']:unregisterAction() -- all from calling resource
Query registered actions with optional filters.
Syntaxexports['onex-voiceInteraction']:queryActions(filter)
| Name | Type | Description |
|---|---|---|
filter.name | string | Match by exact action name |
filter.group | string | Match by group name |
filter.tag | string | Match by tag |
filter.enabled | boolean | Match by enabled state |
filter.resource | string | Match by source resource name |
Returns: { success = true, data = { actions = { ... }, count = 5 } }
Your Scriptlocal result = exports['onex-voiceInteraction']:queryActions({ group = "voice_shop_1", enabled = true }) print("Active shop actions:", result.data.count)
Enable or disable a single action, a named group, or all actions.
Syntaxexports['onex-voiceInteraction']:setActionEnabled(target, enabled)
| Name | Type | Description |
|---|---|---|
target | string | Action name, "#group_name" for a group, "*" for all |
enabled | boolean | true to enable, false to disable |
Returns: { success = true, data = { affected = 1 } }
Your Scriptexports['onex-voiceInteraction']:setActionEnabled("buy_water", false) exports['onex-voiceInteraction']:setActionEnabled("#voice_shop_1", false) exports['onex-voiceInteraction']:setActionEnabled("*", true)
Get the enabled state of an action or group.
Syntaxexports['onex-voiceInteraction']:getActionEnabled(target)
| Name | Type | Description |
|---|---|---|
target | string | Action name, "#group_name", or omit for full disabled state |
Returns (action/group): { success = true, data = { enabled = true } }
Returns (nil target): { success = true, data = { disabledActions = { ... }, disabledGroups = { ... } } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getActionEnabled("buy_water") print(r.data.enabled) -- true or false
Register a custom layer for voice context management.
Syntaxexports['onex-voiceInteraction']:registerLayer(config)
| Name | Type | Description |
|---|---|---|
config.name | string | Unique layer identifier |
config.priority | number | Conflict resolution priority (default 50) |
config.exclusive | boolean | Only one group active at a time (default true) |
config.disableAmbient | boolean | Disable ambient layer when active (default true) |
config.disableLayers | string[] | Other layers to disable when entered |
config.promptHints | string[] | STT vocabulary hints for improved recognition |
config.proximityPriority | boolean | Enable proximity-based layer yielding — closer entity wins conflicts |
Returns: { success = true, data = { name = "shop" } }
Your Scriptexports['onex-voiceInteraction']:registerLayer({ name = "shop", priority = 60, exclusive = true, disableAmbient = true, disableLayers = { "npc_interact" }, promptHints = { "buy", "sell", "price" } })
Activate a layer with a specific action group.
Syntaxexports['onex-voiceInteraction']:enterLayer(layerName, groupName, options)
| Name | Type | Description |
|---|---|---|
layerName | string | Registered layer name |
groupName | string | Group name to activate (matches group on registered actions) |
options.promptHints | string[] | Override STT hints for this session |
options.customHints | table | Bypass automatic hint collection with explicit hint objects |
Returns: { success = true, data = { layer = "shop", group = "voice_shop_1" } }
Your Scriptexports['onex-voiceInteraction']:enterLayer("shop", "voice_shop_liquor_1")
Deactivate a layer and restore previous state.
Syntaxexports['onex-voiceInteraction']:exitLayer(layerName)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name to exit |
Returns: { success = true, data = { layer = "shop" } }
Your Scriptexports['onex-voiceInteraction']:exitLayer("shop")
Switch to a different action group within an already-active layer.
Syntaxexports['onex-voiceInteraction']:setLayerGroup(layerName, groupName)
| Name | Type | Description |
|---|---|---|
layerName | string | Active layer name |
groupName | string | New group to activate |
Returns: { success = true, data = { layer = "banking", group = "voice_bank_confirm" } }
Your Scriptexports['onex-voiceInteraction']:setLayerGroup("banking", "voice_bank_confirm")
Enable or disable a layer globally.
Syntaxexports['onex-voiceInteraction']:setLayerEnabled(layerName, enabled)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name |
enabled | boolean | Enable or disable |
Returns: { success = true, data = { layer = "npc_interact", enabled = false } }
Your Scriptexports['onex-voiceInteraction']:setLayerEnabled("npc_interact", false)
Update a layer's priority or conflict rules at runtime.
Syntaxexports['onex-voiceInteraction']:setLayerConfig(layerName, config)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name |
config.priority | number | New priority value |
config.disableAmbient | boolean | Update ambient disable rule |
config.disableLayers | string[] | Replace conflicting layers list |
Returns: { success = true, data = { layer = "shop" } }
Your Scriptexports['onex-voiceInteraction']:setLayerConfig("shop", { priority = 70 })
Override the visible voice hints for the current session of an active layer.
Syntaxexports['onex-voiceInteraction']:setLayerHints(layerName, hints)
| Name | Type | Description |
|---|---|---|
layerName | string | Active layer name |
hints | table[] | Array of { name, phrase, layer? } hint objects |
Returns: { success = true, data = { layer = "shop", count = 2 } }
Your Scriptexports['onex-voiceInteraction']:setLayerHints("shop", { { name = "buy_item", phrase = "buy water", layer = "shop" }, { name = "check_price", phrase = "how much", layer = "shop" } })
Update the proximity distance for a layer registered with proximityPriority. The layer with the smallest distance wins conflicts.
Syntaxexports['onex-voiceInteraction']:setLayerProximity(layerName, distance)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name |
distance | number | nil | Distance to nearest target, or nil to clear proximity |
Returns: { success = true, data = { layer = "npc_interact" } }
Your Scriptlocal dist = #(GetEntityCoords(PlayerPedId()) - npcCoords) exports['onex-voiceInteraction']:setLayerProximity("npc_interact", dist) -- Clear when done exports['onex-voiceInteraction']:setLayerProximity("npc_interact", nil)
Check whether a layer should yield to a closer proximity competitor. Returns true if another active layer with proximityPriority has a smaller distance.
Syntaxexports['onex-voiceInteraction']:shouldLayerYield(layerName)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name to check |
Returns: boolean
Your Scriptif exports['onex-voiceInteraction']:shouldLayerYield("npc_interact") then return -- another NPC is closer, skip activation end exports['onex-voiceInteraction']:enterLayer("npc_interact", "voice_npc_1")
Get the currently active custom layer and group.
Syntaxexports['onex-voiceInteraction']:getCurrentLayer()
Returns: { success = true, data = { layer = "shop", group = "voice_shop_1" } } — data is nil if no custom layer is active.
Your Scriptlocal r = exports['onex-voiceInteraction']:getCurrentLayer() if r.data then print("In layer:", r.data.layer, "group:", r.data.group) end
Get the current state of a specific layer.
Syntaxexports['onex-voiceInteraction']:getLayerState(layerName)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name to inspect |
Returns: { success = true, data = { enabled, activeGroup, priority, exclusive, disableAmbient, disableLayers } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getLayerState("shop") print("shop priority:", r.data.priority)
Get all registered layers and their current state.
Syntaxexports['onex-voiceInteraction']:getLayers()
Returns: { success = true, data = { layers = { [name] = { enabled, activeGroup, priority, ... } } } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getLayers() for name, layer in pairs(r.data.layers) do print(name, layer.priority, layer.enabled) end
Synthesize and play TTS audio — global or 3D positioned.
Syntaxexports['onex-voiceInteraction']:speak(text, options)
| Name | Type | Description |
|---|---|---|
text | string | Text to speak |
options.voice | string | table | Voice name string, or { name, rate, pitch, volume } object |
options.rate | number | Speech rate shorthand (default 1.0) — overridden by voice.rate if both set |
options.pitch | number | Pitch shift shorthand (default 1.0) — overridden by voice.pitch if both set |
options.volume | number | Volume level shorthand (default 1.0) — overridden by voice.volume if both set |
options.entity | number | Ped entity handle for 3D positioned audio |
options.maxDistance | number | Hearing range for 3D audio (default 30.0) |
options.animation | table | false | false to disable all animation, or { lipSync, gesture, gestureDict, gestureAnim, returnToIdle, idleScenario } |
options.showSubtitle | boolean | Display subtitle synced to TTS duration |
options.immediateSubtitle | boolean | Display subtitle immediately without waiting for TTS timing |
Returns: { success = true, data = { soundId = "sound_12345_6789", is3D = true } }
Your Script-- Global TTS exports['onex-voiceInteraction']:speak("Hello world", { voice = { name = "en-US-GuyNeural", rate = 1.0, pitch = 1.0 } }) -- 3D NPC speech(voice auto-detected from NPC config) exports['onex-voiceInteraction']:speak("Hey there!", { entity = pedHandle, maxDistance = 30.0 }) -- Custom animation with subtitle exports['onex-voiceInteraction']:speak("Step right up!", { entity = pedHandle, animation = { lipSync = true, gesture = true, gestureDict = "gestures@m@standing@casual", gestureAnim = "gesture_hand_up" }, showSubtitle = true })
Stop TTS playback — all sounds or a specific one.
Syntaxexports['onex-voiceInteraction']:stopSpeech(soundId)
| Name | Type | Description |
|---|---|---|
soundId | string | Specific 3D sound ID to stop, or omit to stop everything |
Returns: { success = true, data = { stopped = 1 } }
Your Scriptexports['onex-voiceInteraction']:stopSpeech() -- stop all exports['onex-voiceInteraction']:stopSpeech("sound_12345_6789") -- stop one
Retrieve the available TTS voice list from the browser engine.
Syntaxexports['onex-voiceInteraction']:getVoices(callback, timeout)
| Name | Type | Description |
|---|---|---|
callback | function | function(result) called with { success, data = { voices } } |
timeout | number | Timeout in milliseconds (default 5000) |
Without callback: returns { success = true, data = { pending = true, listenEvent = "onex-voiceInteraction:tts:voices" } }
Your Scriptexports['onex-voiceInteraction']:getVoices(function(result) if result.success then for _, voice in ipairs(result.data.voices) do print(voice.name) end end end, 5000)
Get the configured or auto-detected TTS voice for a ped.
Syntaxexports['onex-voiceInteraction']:getVoiceForPed(entity)
| Name | Type | Description |
|---|---|---|
entity | number | Ped entity handle |
Returns: { success = true, data = { voice = "en-US-GuyNeural", gender = "male", cached = false } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getVoiceForPed(pedHandle) if r.success then print("Voice:", r.data.voice, "Gender:", r.data.gender) end
Clear the voice assignment cache for a specific ped or all peds.
Syntaxexports['onex-voiceInteraction']:clearVoiceCache(entity)
| Name | Type | Description |
|---|---|---|
entity | number | Ped entity handle, or omit to clear all |
Returns: { success = true, data = { cleared = 1 } }
Your Scriptexports['onex-voiceInteraction']:clearVoiceCache(pedHandle) -- one ped exports['onex-voiceInteraction']:clearVoiceCache() -- all
Get complete NPC configuration and defaults for an entity or model name.
Syntaxexports['onex-voiceInteraction']:getNPCInfo(target)
| Name | Type | Description |
|---|---|---|
target | number | string | Ped entity handle or model name string (e.g. "mp_m_freemode_01") |
Returns: { success = true, data = { config, voice, animation, audio, personality, traits, gender, isDefault } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getNPCInfo(pedHandle) if r.success then print("Voice:", r.data.voice.voice) print("Is default config:", r.data.isDefault) end
Check whether an NPC has specific traits. Optional traits are probability-rolled and cached.
Syntaxexports['onex-voiceInteraction']:checkNPCTraits(entity, traitIds)
| Name | Type | Description |
|---|---|---|
entity | number | Ped entity handle |
traitIds | string | string[] | Single trait ID or array of trait IDs |
Returns: { success = true, data = { [traitId] = { hasTrait, isRequired, probability, rolledProbability, cached } } }
| Field | Type | Description |
|---|---|---|
hasTrait | boolean | Whether the NPC has this trait |
isRequired | boolean | Whether the trait is required (not probability-based) |
probability | number | Configured probability (0.0–1.0) |
rolledProbability | number | Actual rolled value used to determine hasTrait |
cached | boolean | Whether this result came from the roll cache |
Your Scriptlocal r = exports['onex-voiceInteraction']:checkNPCTraits(pedHandle, { "drug_addict", "aggressive" }) if r.data.drug_addict.hasTrait then -- respond accordingly end
Query traits by entity, tag, or definition name.
Syntaxexports['onex-voiceInteraction']:queryNPCTraits(query)
| Name | Type | Description |
|---|---|---|
query.entity | number | Get all traits for a specific ped |
query.tag | string | Filter by trait tag (combinable with entity) |
query.definition | string | true | Get a specific trait definition, or true for all |
Your Script-- All traits on one NPC local r = exports['onex-voiceInteraction']:queryNPCTraits({ entity = pedHandle }) -- NPCs with "criminal" tagged traits local r = exports['onex-voiceInteraction']:queryNPCTraits({ tag = "criminal" }) -- Single trait definition local r = exports['onex-voiceInteraction']:queryNPCTraits({ definition = "drug_addict" })
Clear cached trait roll results for a ped, forcing a re-roll on the next checkNPCTraits call.
Syntaxexports['onex-voiceInteraction']:clearTraitCache(entity)
| Name | Type | Description |
|---|---|---|
entity | number | Ped entity handle — required, clears only this ped |
Returns: { success = true, data = { cleared = 1 } }
Your Scriptexports['onex-voiceInteraction']:clearTraitCache(pedHandle)
Last updated 14 days ago