Configuration (config.lua)
All MBT Emote Menu settings live in config.lua. There are no in-game admin panels — every option is set here and applied on resource start.
Quick Overview
| Section | Purpose |
|---|---|
MBT.Language / MBT.Debug | Locale selection and debug logging |
MBT.RpemotesResource | rpemotes resource name (auto-detected by default) |
MBT.Menu | Keybind, command, layout, position, auto-close, remember state, watermark, native menu override |
MBT.Features | Toggle favorites, recent emotes, quick bind, emote wheel, shared popup, preview ped, placement, social features |
MBT.EmoteWheel | Wheel key, slot count, remove key, radial/linear mode |
MBT.Personas | Named loadouts (Quick Binds + Wheel) — max persona count |
MBT.Trending | Server-wide trending emote spotlight |
MBT.OpenJoin | Anonymous group emote join settings |
MBT.AntiSpam | Cooldown between emote plays |
MBT.RpText | /me and /do styled pills above player heads |
MBT.PhotoMode | Cinematic orbit camera with filters and optional Discord upload |
MBT.BannedEmotes | Server-side emote blocklist |
MBT.Categories | Category visibility and display order |
MBT.Theme | Six hex color tokens sent to the NUI |
MBT.Ecosystem | Integration with other MBT scripts |
MBT.JobPermissions | Per-emote job restrictions with framework auto-detection |
MBT.Notification | Notification function wrapper (ox_lib, ESX, QB, etc.) |
Global Settings
MBT.Language
Type: string — Default: 'en'
Locale used for all UI text. Supported values: 'en', 'it', 'es', 'fr', 'de', 'pt'. Additional locales can be added by creating a file in the locales/ folder.
MBT.Debug
Type: boolean — Default: false
Enables verbose console logging for development. Set to false on production servers.
MBT.RpemotesResource
Type: string | nil — Default: nil
The resource name of the rpemotes engine to integrate with. When nil, auto-detection tries rpemotes-reborn, then rpemotes, then rp-emotes. Force a specific name only if auto-detection fails.
Menu Settings (MBT.Menu)
| Key | Type | Default | Description |
|---|---|---|---|
Keybind | string | 'F4' | Key that opens/closes the MBT emote menu |
Command | string | 'mbt_emotes' | Chat command alternative to the keybind |
Layout | string | 'cinematic' | UI layout: 'cinematic' or 'default' |
Position | string | 'right' | Panel side: 'left' or 'right' |
CloseOnPlay | boolean | true | Close the menu automatically when an emote starts playing |
RememberState | boolean | true | Remember scroll position, active tab, and filters after an emote plays. Resets on ESC/X |
Watermark | boolean | true | Show the MBT watermark in the top corner of the UI |
OverrideNativeMenu | boolean | true | Suppress rpemotes' NativeUI menu and redirect its key to MBT's menu |
Features (MBT.Features)
| Key | Type | Default | Description |
|---|---|---|---|
Favorites | boolean | true | Enable the favourites tab and star button per emote |
RecentEmotes | boolean | true | Track and display recently played emotes in the Recent tab |
MaxRecent | number | 12 | Maximum number of recent emotes to keep |
QuickBind | boolean | true | Allow drag-to-bind from the menu onto numpad slots (NUM1–NUM6) |
SharedPopup | boolean | true | Show an inline popup when a shared emote invitation is received |
PreviewPed | boolean | true | Render an animated ped preview when clicking the eye icon on an emote card |
EmoteWheel | boolean | true | Enable the hold-to-peek emote wheel |
EmotePlacement | boolean | true | Enable the "Place in world" 📍 button on emote cards |
OpenJoin | boolean | true | Enable anonymous proximity group emotes (Open Join) |
WhatsThat | boolean | false | Show a bubble above nearby emoting players with the emote name and a copy hotkey |
AdultEmotes | boolean | false | Include emotes flagged as AdultAnimation in rpemotes-reborn |
AbusableEmotes | boolean | false | Include walk styles flagged as abusable (movement exploits) |
Emote Wheel (MBT.EmoteWheel)
| Key | Type | Default | Description |
|---|---|---|---|
Key | string | 'K' | Hold this key to open the emote wheel |
Slots | number | 8 | Number of slots on the wheel (maximum 8) |
RemoveKey | string | 'X' | Press while the wheel is open to remove the emote from the current slot |
Mode | string | 'radial' | 'radial' — move the mouse toward a slot to select it; 'linear' — scroll through slots |
PointerSensitivity | number | 2.8 | Radial mode only: how fast the flick pointer moves (higher = faster) |
Personas (MBT.Personas)
Named loadouts bundling Quick Bind slots (NUM1–NUM6) and Emote Wheel slots. Players create and switch between personas from the Settings panel.
| Key | Type | Default | Description |
|---|---|---|---|
Max | number | 4 | Maximum number of personas a player can create |
Personas are stored per-player via KVP. Switching a persona instantly replaces all Quick Bind and Wheel slot assignments.
Trending (MBT.Trending)
Controls the server-wide "Trending this week" hero card shown at the top of the browse view.
| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | true | Show the trending emote spotlight |
WindowDays | number | 7 | Rolling window length in days |
MinPlays | number | 10 | Minimum play count within the window for an emote to qualify |
SaveIntervalMinutes | number | 10 | How often play counts are flushed to KVP |
Trending counts are aggregate — the server records how many times each emote was played across all players. No per-player tracking is stored.
Open Join (MBT.OpenJoin)
Controls the anonymous group emote join mechanic. When you start an eligible emote, nearby players receive a pill invitation to join.
| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | true | Master toggle for Open Join |
Radius | number | 8.0 | Distance in meters for eligible recipients |
MaxRecipients | number | 30 | Maximum players to notify — prevents storm in crowded areas |
AnnounceCooldownMs | number | 3000 | Minimum milliseconds between announcements from the same player |
PillKey | string | 'Y' | Key shown in the pill for joining |
Categories | table | { 'Dances', 'Shared', ... } | Emote categories that trigger Open Join broadcasts |
Players can opt out at any time with /mbt_openjoin off and opt back in with /mbt_openjoin on. The preference persists across sessions.
Anti-Spam (MBT.AntiSpam)
| Key | Type | Default | Description |
|---|---|---|---|
CooldownMs | number | 250 | Minimum milliseconds between consecutive emote plays on the same client |
RP Text (MBT.RpText)
Floating styled pills above players' heads for /me and /do style commands. Multiple pills per player are stacked vertically. Text is passed to the React layer as data — no HTML injection is possible.
MBT.RpText = {
Enabled = true,
MaxLength = 110, -- max characters per message
DurationMs = 6500, -- how long the pill stays visible
ThrottleMs = 1000, -- per-player cooldown between sends
HeadOffset = 0.25, -- pill height above the head bone (metres)
Channels = {
{ id = 'me', command = 'me', label = 'ME', range = 16.0, color = '00e676' },
{ id = 'do', command = 'do', label = 'DO', range = 16.0, color = '7fa8c9' },
-- { id = 'med', command = 'med', label = 'MED', range = 24.0, color = 'e0654f' },
},
}Each entry in Channels:
| Field | Description |
|---|---|
id | Internal channel identifier |
command | Chat command registered in-game (e.g. /me) |
label | Pill tag shown before the message |
range | Visibility radius in metres |
color | Hex accent for the pill tag (no #) |
If another MBT resource (e.g. mbt_hud) also manages /me and /do, it will auto-detect this module via the ProvidesRpText export and defer — no conflict or duplicate commands.
Photo Mode (MBT.PhotoMode)
A cinematic orbit camera opened from a button in the emote menu. Players can orbit, zoom, adjust FOV, toggle depth-of-field, and apply look filters.
MBT.PhotoMode = {
Enabled = true,
Watermark = true, -- show MBT watermark on the framing overlay
LogoUrl = '', -- your server logo URL (Discord embed thumbnail)
Caption = 'Captured in Photo Mode',
OrbitSensitivity = 0.45,
ZoomSensitivity = 0.30,
MinDistance = 0.7, -- metres
MaxDistance = 7.0,
DofDefault = true, -- start with depth-of-field on
Filters = {
{ id = 'none', label = 'None', timecycle = nil },
{ id = 'cinema', label = 'Cinematic', timecycle = 'cinema', strength = 0.55 },
{ id = 'noir', label = 'Noir', timecycle = 'phone_cam11', strength = 1.0 },
{ id = 'warm', label = 'Warm', timecycle = 'phone_cam1', strength = 1.0 },
{ id = 'vibrant', label = 'Vibrant', timecycle = 'phone_cam2', strength = 1.0 },
{ id = 'cool', label = 'Cool', timecycle = 'phone_cam4', strength = 1.0 },
},
Discord = {
Enabled = false,
WebhookUrl = '', -- Discord webhook URL
ThrottleMs = 30000, -- per-player cooldown between Discord uploads
Style = 'embed', -- 'image' = screenshot only · 'embed' = rich card with player info
},
}The Send to Discord button requires the screenshot-basic resource to be installed and started. Without it the button is shown but posting fails gracefully with an error in the NUI.
Banned Emotes (MBT.BannedEmotes)
A list of emote names to filter out server-side. Matching is case-insensitive.
MBT.BannedEmotes = {
'twerk',
'abusableWalk',
}Banned emotes are removed from the catalog before it is sent to clients, and are also blocked from Open Join broadcasts. This does not affect rpemotes' own /e <name> chat command — enforcing that requires restricting the rpemotes ACE permission upstream.
Categories (MBT.Categories)
An ordered array of category definitions. Each entry has:
| Field | Type | Description |
|---|---|---|
type | string | Internal rpemotes category key — do not change |
label | string | Display name shown in the tab bar |
icon | string | Lucide icon name rendered next to the label |
visible | boolean | Set to false to hide the category from the menu |
Default order: Emotes → Props → Dances → Shared → Expressions → Walk Styles → Animals → Emojis.
Reorder entries to change the tab order. Hidden categories are still accessible via search.
Theme (moved in 1.8)
The colours are no longer in config.lua. They ship in default.lua, and you change them in game — shield icon → Settings → pick a colour → Apply. It applies to everyone on the server immediately, with no restart, and survives one because it is stored server-side.
One switch stays in config.lua, because it is a policy and not a colour:
MBT.Theme = MBT.Theme or {}
MBT.Theme.AllowAccentChange = false -- true = players may pick their own accent presetThe shipped values live in default.lua:
| Key | Default | Purpose |
|---|---|---|
Accent | '00e676' | The one that matters — the admin panel overrides it per server |
AllowAccentChange | false | Whether players may pick their own accent preset |
Background | '0C0E14' | Legacy, unused |
Card | '121814' | Legacy, unused |
Text | 'E8E8EE' | Primary text |
SubText | '8A93A6' | Secondary text |
Border | '1A1D26' | Internal dividers |
Background and Card no longer drive anything — the surfaces derive from the accent now. They are left in place rather than removed mid-release.
default.lua is ours, not yours: an update overwrites it. Editing it works, but anything you change there comes back on the next release. config.lua is the file we never touch — it loads after default.lua, so anything you set there still wins.
Every surface derives from the accent. The panels are not a fixed colour with a coloured highlight on top — they carry the accent's own hue at a fixed lightness, so choosing amber turns the cards amber and choosing a near-grey leaves them near-grey. Text stays between 14:1 and 15:1 against the panel for every colour, because only the hue moves.
The picker shows the contrast of your accent against the panel and warns below 3:1 — that is where the state lives (ready slots, active chips), so a colour that sinks into its own panel stops being readable. It warns; it never blocks. Reset clears the stored choice and returns to the shipped value, deleting the key rather than writing today's default into it, so a future release that ships a different colour still reaches you.
Admin (MBT.Admin)
Everything behind the shield icon — the scene editor, the settings panel, the update notice — is gated by one ACE permission, checked server-side. A player without it never receives the data at all.
| Key | Type | Default | Description |
|---|---|---|---|
Command | string | 'mbt_emote_menu' | Chat command that opens the scene editor |
Permission | string | nil | ACE object required. nil resolves to command. + Command |
UpdateNotice.Enabled | boolean | true | "Update available" notice inside the settings popover, shown only to admins. The console line prints regardless |
UpdateNotice.Repository | string | 'MalibuTechTeam/mbt_emote_menu' | Repository queried for the version check |
Editor.Enabled | boolean | true | Master toggle for the in-game scene editor |
Editor.MaxMarks | number | 12 | Maximum marks per scene |
Editor.MaxScenes | number | 200 | Maximum scenes per server |
The editor stores scenes in MySQL (table mbt_emote_menu_scenes, created automatically on first boot), so they survive script updates and live in your normal database backups. It requires oxmysql: without it the editor stays off, says so in console, and the rest of the menu is unaffected.
Venue Spots (MBT.VenueSpots)
Scenes and spots authored with the in-game editor. The definitions live in the database, not in this file — an owner places them in the world instead of typing coordinates. This section only controls how they behave once placed.
| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | true | Master toggle for placed scenes and spots |
PollMs | number | 750 | How often the client checks whether you walked into one |
Key | string | 'E' | Key shown in the prompt; the control itself is E |
CountdownFrom | number | 5 | Seconds counted down before a multi-actor scene fires |
SnapToMark | boolean | true | Place the player exactly on the mark before the emote runs |
CountdownFrom is bracing time, not a race start: everyone is already standing on their mark by then, and too short a count begins the shot before anyone has looked up.
SnapToMark is the point of authoring a position — an emote that leans on a counter only looks right from the spot and angle it was placed at. Turn it off only if you would rather the emote played wherever the player happens to be standing.
MBT Ecosystem Integration (MBT.Ecosystem)
| Key | Type | Default | Description |
|---|---|---|---|
MetaClothes | boolean | false | Set to true if mbt_meta_clothes v2 is installed |
WearableProps | boolean | false | Set to true if mbt_wearable_props is installed |
When enabled, the UI can surface relevant clothing/prop context while browsing emotes.
Job Permissions (MBT.JobPermissions)
| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | true | Master toggle — disable to allow all players access to all emotes |
Framework | string | 'auto' | Framework for job lookups: 'auto', 'esx', 'qbox', 'qbcore', or 'standalone' |
Emotes | table | {} | Map of emote name → allowed job list |
'auto' detection order: ESX → QBox → QBCore → standalone.
Example:
MBT.JobPermissions = {
Enabled = true,
Framework = 'auto',
Emotes = {
['handcuff'] = { 'police', 'sheriff' },
['mechanic'] = { 'mechanic', 'bennys' },
},
}Players without a matching job will see the emote greyed out with a lock icon.
Notification (MBT.Notification)
A callback function called whenever MBT Emote Menu needs to send a player notification. data contains title, description, type, and duration. Uncomment or write your own integration:
MBT.Notification = function(data)
-- ox_lib
exports.ox_lib:notify({ title = data.title, description = data.description, type = data.type })
-- ESX
-- ESX.ShowNotification(data.description)
-- QBCore
-- QBCore.Functions.Notify(data.description, 'primary')
end