Layers provide priority-based context management for voice commands. When a player enters a shop, bank, or minigame, only the commands relevant to that context are active. Global commands (system, emergency) always work regardless of context.
Only one custom layer can be active at a time.
| Layer | Priority | Exclusive | Always active | Description |
|---|---|---|---|---|
global | 100 | no | yes | System commands — always available when layer is enabled |
context | 50 | yes | no | Generic exclusive context for backward compatibility |
ambient | 10 | no | yes | Default commands when no custom layer is active |
The ambient layer auto-disables when an active custom layer has disableAmbient = true. The global layer is never auto-disabled by other layers.
Register a custom layer. Call once at resource start — layers persist until the resource stops.
Syntaxexports['onex-voiceInteraction']:registerLayer(config)
| Name | Type | Default | Description |
|---|---|---|---|
config.name | string | — | Unique layer identifier. Cannot be "global" or "ambient". |
config.priority | number | 50 | Higher values take precedence in conflict resolution. |
config.exclusive | boolean | true | When true, only one group can be active at a time within this layer. |
config.disableAmbient | boolean | true | Automatically disables the ambient layer when this layer is entered. |
config.disableLayers | string[] | {} | Other layer names to disable when this layer is entered. Restored on exit. |
config.promptHints | string[] | nil | STT vocabulary hints — words the engine will prioritize recognizing. |
config.proximityPriority | boolean | nil | Enable proximity-based layer yielding — the layer with the smallest reported distance wins conflicts. |
Returns: { success = true, data = { name = "shop" } }
Your Scriptexports['onex-voiceInteraction']:registerLayer({ name = "shop", priority = 60, exclusive = true, disableAmbient = true, disableLayers = { "npc_interact", "ambient_npc" }, promptHints = { "buy", "sell", "price", "how much" } })
Activate a layer with a specific action group. Actions registered with group = groupName and layer = layerName become active.
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 the layer's registered STT hints for this session only |
options.customHints | table | Bypass automatic hint collection with explicit hint objects { name, phrase, layer? } |
Returns: { success = true, data = { layer = "shop", group = "voice_shop_liquor_1" } }
What happens internally:
disableLayersdisableAmbient = trueYour Scriptexports['onex-voiceInteraction']:enterLayer("shop", "voice_shop_liquor_1") -- With hint override for this session exports['onex-voiceInteraction']:enterLayer("shop", "voice_shop_liquor_1", { promptHints = { "buy", "water", "beer" } })
Deactivate a layer and restore the state that existed before enterLayer was called.
Syntaxexports['onex-voiceInteraction']:exitLayer(layerName)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name to exit |
Returns: { success = true, data = { layer = "shop" } }
What happens internally:
Your Scriptexports['onex-voiceInteraction']:exitLayer("shop")
Switch the active action group within an already-entered layer. Used for multi-step flows where different commands apply at different stages.
Syntaxexports['onex-voiceInteraction']:setLayerGroup(layerName, groupName)
| Name | Type | Description |
|---|---|---|
layerName | string | Active layer name — must be the currently active custom layer |
groupName | string | New group to activate |
Returns: { success = true, data = { layer = "banking", group = "voice_bank_confirm" } }
Your Script-- Player is in banking — normal operations exports['onex-voiceInteraction']:enterLayer("banking", "voice_bank_main") -- Player says "withdraw 500" → switch to confirmation exports['onex-voiceInteraction']:setLayerGroup("banking", "voice_bank_main_confirm") -- Now only "yes"/"no"/"cancel" commands are active -- After confirmation → back to normal exports['onex-voiceInteraction']:setLayerGroup("banking", "voice_bank_main")
Enable or disable a layer globally. Disabled layers cannot be entered.
Syntaxexports['onex-voiceInteraction']:setLayerEnabled(layerName, enabled)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name |
enabled | boolean | true to enable, false to disable |
Returns: { success = true, data = { layer = "npc_interact", enabled = false } }
Your Script-- Temporarily block NPC interaction contexts exports['onex-voiceInteraction']:setLayerEnabled("npc_interact", false) -- Re-enable later exports['onex-voiceInteraction']:setLayerEnabled("npc_interact", true)
Update a layer's configuration at runtime. Only the provided fields are overwritten.
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 the conflicting layers list |
Returns: { success = true, data = { layer = "shop" } }
Your Scriptexports['onex-voiceInteraction']:setLayerConfig("shop", { priority = 70, disableLayers = { "npc_interact", "ambient_npc", "robbery" } })
Override the visible voice hints for the current active session of a layer. Bypasses automatic hint collection from the action registry.
Syntaxexports['onex-voiceInteraction']:setLayerHints(layerName, hints)
| Name | Type | Description |
|---|---|---|
layerName | string | Active layer name |
hints | table[] | Array of hint objects { name, phrase, layer? } |
The layer must currently be active (enterLayer must have been called). Hints are cleared automatically on exitLayer.
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" }, { name = "leave_shop", phrase = "goodbye", layer = "shop" } })
Update the proximity distance for a layer registered with proximityPriority. The layer instance reporting the smallest distance wins activation conflicts against other proximity-priority layers of the same type.
Syntaxexports['onex-voiceInteraction']:setLayerProximity(layerName, distance)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name |
distance | number | nil | Distance to the nearest relevant entity, or nil to clear |
Returns: { success = true, data = { layer = "npc_interact" } }
Your Scriptlocal dist = #(GetEntityCoords(PlayerPedId()) - npcCoords) exports['onex-voiceInteraction']:setLayerProximity("npc_interact", dist) -- Clear when player walks away exports['onex-voiceInteraction']:setLayerProximity("npc_interact", nil)
Check whether a layer should yield to a closer proximity competitor before entering. Returns true if another active layer with proximityPriority has a smaller reported distance. Use this before calling enterLayer to avoid overriding a closer NPC interaction.
Syntaxexports['onex-voiceInteraction']:shouldLayerYield(layerName)
| Name | Type | Description |
|---|---|---|
layerName | string | Layer name to check |
Returns: boolean
Your Scriptlocal dist = #(GetEntityCoords(PlayerPedId()) - npcCoords) exports['onex-voiceInteraction']:setLayerProximity("npc_interact", dist) if exports['onex-voiceInteraction']:shouldLayerYield("npc_interact") then return -- another NPC is closer, skip activation end exports['onex-voiceInteraction']:enterLayer("npc_interact", "voice_npc_" .. npcId)
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("Active layer:", r.data.layer, "Group:", r.data.group) else print("No custom layer active") end
Get the full 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") if r.success then print("Priority:", r.data.priority) print("Active group:", r.data.activeGroup) print("Enabled:", r.data.enabled) end
Get all registered layers and their current state.
Syntaxexports['onex-voiceInteraction']:getLayers()
Returns: { success = true, data = { layers = { [name] = { enabled, activeGroup, priority, exclusive, disableAmbient, disableLayers } } } }
Your Scriptlocal r = exports['onex-voiceInteraction']:getLayers() for name, layer in pairs(r.data.layers) do print(name, "priority:", layer.priority, "enabled:", layer.enabled) end
When voice input arrives, the layer system filters which actions are eligible before matching:
| Action layer | Active when |
|---|---|
"global" | Always, as long as the layer itself is enabled |
"ambient" | No custom layer is active, or active layer has disableAmbient = false |
| custom exclusive | Is the current activeCustomLayer AND group matches activeGroup |
| custom non-exclusive | Layer is enabled AND group matches activeGroup |
Actions in disabled layers or non-matching groups are excluded before matching starts.
Your Scriptexports['onex-voiceInteraction']:registerLayer({ name = "shop", priority = 60, exclusive = true, disableAmbient = true, disableLayers = { "npc_interact", "ambient_npc" } })
Your ScriptRegisterAction({ name = "shop_liquor_buy", phrases = { "buy {item}", "i want {item}" }, trigger = { callback = function(data) local item = data.extractedVariables.item TriggerServerEvent('shop:buy', item) end }, layer = "shop", group = "voice_shop_liquor_1" }) RegisterAction({ name = "shop_liquor_leave", phrases = { "goodbye", "see you", "leave" }, trigger = { callback = function(data) exports['onex-voiceInteraction']:exitLayer("shop") end }, layer = "shop", group = "voice_shop_liquor_1" })
Your Script-- Player enters shop zone exports['onex-voiceInteraction']:enterLayer("shop", "voice_shop_liquor_1") -- Only shop commands + global are now active -- ambient, npc_interact, ambient_npc are all disabled
Your Script-- Player leaves shop zone exports['onex-voiceInteraction']:exitLayer("shop") -- All previously disabled layers are restored
Last updated 14 days ago