Onex
Onex
Home
Products
Documentation
Changelog
Login with FiveM
  • 👋 Welcome
  • Claim Purchase
  • Translations
    • Weaponmeta
    • Voice Interaction
      Integrations
      Installation
      Configuration
      Features
      Api
      Action
      Events
      Npc
      Speech
Artifacts Tracker
  1. Onex Scripts
  2. Scripts_guides / Layer System
Ctrl K

LAYER SYSTEM#

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.

Built-in layers#

LayerPriorityExclusiveAlways activeDescription
global100noyesSystem commands — always available when layer is enabled
context50yesnoGeneric exclusive context for backward compatibility
ambient10noyesDefault 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.

registerLayer#

Register a custom layer. Call once at resource start — layers persist until the resource stops.

Syntax
exports['onex-voiceInteraction']:registerLayer(config)
NameTypeDefaultDescription
config.namestring—Unique layer identifier. Cannot be "global" or "ambient".
config.prioritynumber50Higher values take precedence in conflict resolution.
config.exclusivebooleantrueWhen true, only one group can be active at a time within this layer.
config.disableAmbientbooleantrueAutomatically disables the ambient layer when this layer is entered.
config.disableLayersstring[]{}Other layer names to disable when this layer is entered. Restored on exit.
config.promptHintsstring[]nilSTT vocabulary hints — words the engine will prioritize recognizing.
config.proximityPrioritybooleannilEnable proximity-based layer yielding — the layer with the smallest reported distance wins conflicts.

Returns: { success = true, data = { name = "shop" } }

Your Script
exports['onex-voiceInteraction']:registerLayer({ name = "shop", priority = 60, exclusive = true, disableAmbient = true, disableLayers = { "npc_interact", "ambient_npc" }, promptHints = { "buy", "sell", "price", "how much" } })

enterLayer#

Activate a layer with a specific action group. Actions registered with group = groupName and layer = layerName become active.

Syntax
exports['onex-voiceInteraction']:enterLayer(layerName, groupName, options)
NameTypeDescription
layerNamestringRegistered layer name
groupNamestringGroup name to activate — matches group on registered actions
options.promptHintsstring[]Override the layer's registered STT hints for this session only
options.customHintstableBypass automatic hint collection with explicit hint objects { name, phrase, layer? }

Returns: { success = true, data = { layer = "shop", group = "voice_shop_liquor_1" } }

What happens internally:

  1. Validates layer exists and is not globally disabled
  2. Saves previous state (for restoration on exit)
  3. Disables layers listed in disableLayers
  4. Disables ambient if disableAmbient = true
  5. Sets active group and enables that group's actions
  6. Invalidates the command matching cache
  7. Refreshes the voice hints display
Your Script
exports['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" } })

exitLayer#

Deactivate a layer and restore the state that existed before enterLayer was called.

Syntax
exports['onex-voiceInteraction']:exitLayer(layerName)
NameTypeDescription
layerNamestringLayer name to exit

Returns: { success = true, data = { layer = "shop" } }

What happens internally:

  1. Disables the active group's actions
  2. Restores previously disabled layers to their original state
  3. Re-enables ambient if it was disabled by this layer
  4. Clears the active group and session hints
  5. Invalidates cache and refreshes hints
Your Script
exports['onex-voiceInteraction']:exitLayer("shop")

setLayerGroup#

Switch the active action group within an already-entered layer. Used for multi-step flows where different commands apply at different stages.

Syntax
exports['onex-voiceInteraction']:setLayerGroup(layerName, groupName)
NameTypeDescription
layerNamestringActive layer name — must be the currently active custom layer
groupNamestringNew 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")

setLayerEnabled#

Enable or disable a layer globally. Disabled layers cannot be entered.

Syntax
exports['onex-voiceInteraction']:setLayerEnabled(layerName, enabled)
NameTypeDescription
layerNamestringLayer name
enabledbooleantrue 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)

setLayerConfig#

Update a layer's configuration at runtime. Only the provided fields are overwritten.

Syntax
exports['onex-voiceInteraction']:setLayerConfig(layerName, config)
NameTypeDescription
layerNamestringLayer name
config.prioritynumberNew priority value
config.disableAmbientbooleanUpdate ambient disable rule
config.disableLayersstring[]Replace the conflicting layers list

Returns: { success = true, data = { layer = "shop" } }

Your Script
exports['onex-voiceInteraction']:setLayerConfig("shop", { priority = 70, disableLayers = { "npc_interact", "ambient_npc", "robbery" } })

setLayerHints#

Override the visible voice hints for the current active session of a layer. Bypasses automatic hint collection from the action registry.

Syntax
exports['onex-voiceInteraction']:setLayerHints(layerName, hints)
NameTypeDescription
layerNamestringActive layer name
hintstable[]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 Script
exports['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" } })

setLayerProximity#

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.

Syntax
exports['onex-voiceInteraction']:setLayerProximity(layerName, distance)
NameTypeDescription
layerNamestringLayer name
distancenumber | nilDistance to the nearest relevant entity, or nil to clear

Returns: { success = true, data = { layer = "npc_interact" } }

Your Script
local dist = #(GetEntityCoords(PlayerPedId()) - npcCoords) exports['onex-voiceInteraction']:setLayerProximity("npc_interact", dist) -- Clear when player walks away exports['onex-voiceInteraction']:setLayerProximity("npc_interact", nil)

shouldLayerYield#

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.

Syntax
exports['onex-voiceInteraction']:shouldLayerYield(layerName)
NameTypeDescription
layerNamestringLayer name to check

Returns: boolean

Your Script
local 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)

getCurrentLayer#

Get the currently active custom layer and group.

Syntax
exports['onex-voiceInteraction']:getCurrentLayer()

Returns: { success = true, data = { layer = "shop", group = "voice_shop_1" } } — data is nil if no custom layer is active.

Your Script
local 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

getLayerState#

Get the full current state of a specific layer.

Syntax
exports['onex-voiceInteraction']:getLayerState(layerName)
NameTypeDescription
layerNamestringLayer name to inspect

Returns: { success = true, data = { enabled, activeGroup, priority, exclusive, disableAmbient, disableLayers, disabledByProximity } }

FieldTypeDescription
enabledbooleanWhether the layer is currently enabled
activeGroupstring | nilCurrently active group, or nil if not entered
prioritynumberLayer priority value
exclusivebooleanWhether the layer is exclusive
disableAmbientbooleanWhether this layer disables ambient when active
disableLayersstring[]Layers disabled when this one is entered
disabledByProximitybooleantrue if disabled because a closer proximity-priority competitor is active
Your Script
local 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

getLayers#

Get all registered layers and their current state.

Syntax
exports['onex-voiceInteraction']:getLayers()

Returns: { success = true, data = { layers = { [name] = { enabled, activeGroup, priority, exclusive, disableAmbient, disableLayers } } } }

Your Script
local r = exports['onex-voiceInteraction']:getLayers() for name, layer in pairs(r.data.layers) do print(name, "priority:", layer.priority, "enabled:", layer.enabled) end

Action filtering rules#

When voice input arrives, the layer system filters which actions are eligible before matching:

Action layerActive when
"global"Always, as long as the layer itself is enabled
"ambient"No custom layer is active, or active layer has disableAmbient = false
custom exclusiveIs the current activeCustomLayer AND group matches activeGroup
custom non-exclusiveLayer is enabled AND group matches activeGroup

Actions in disabled layers or non-matching groups are excluded before matching starts.

Pattern: Shop integration#

Register layer at resource start#

Your Script
exports['onex-voiceInteraction']:registerLayer({ name = "shop", priority = 60, exclusive = true, disableAmbient = true, disableLayers = { "npc_interact", "ambient_npc" } })

Register actions for each shop group#

Your Script
RegisterAction({ 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" })

Enter layer on proximity#

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

Exit layer on departure#

Your Script
-- Player leaves shop zone exports['onex-voiceInteraction']:exitLayer("shop") -- All previously disabled layers are restored

Last updated 3 months ago

Quick Links

All DocumentationOur Products