BLN Native Wheel β

πΊ Preview β
π° Buy Now β
Introducing BLN Wheel Menu - a premium radial menu system built using native RedM functions, offering the smoothest and most authentic experience possible. Say goodbye to laggy NUI-based menus and hello to seamless, game-like interaction!
Why Choose BLN Wheel Menu? β
- Native Implementation - Built using game natives for that authentic RedM feel
- Zero NUI Impact - No performance hit from web-based interfaces
- Pixel-Perfect Recreation - Matches the original game's wheel menu design
- Unlimited Menu Items - No more 8-item limit, create detailed nested menus
- Theme System - Swap between 10 built-in themes or create your own
- Favorites & Hotkeys - Assign items to favorite slots (1-8) and trigger them with a hotkey
- History Mode - Automatically tracks recently used items for quick access
- Emotes Menu - Built-in emotes menu with many emotes ready to use
- Advanced Filtering System - Control access to menu items based on any condition
- Complete Controller Support - Full keyboard, mouse and controller navigation support
- Hybrid Input System - Seamless switching between mouse and keyboard/controller
Quick Setup β
- Add to your resources folder
- Add
ensure bln_wheelto your server.cfg (afterbln_libandbln_notify) - Configure your menu items in
shared/menu.lua - Pick a theme in
shared/config.lua - Enjoy!
Configuration β
Config File (shared/config.lua) β
The config file handles behavior and logic settings. All visual/UI settings are managed through the theme system.
Config = {
showMode = 'hold', -- 'toggle' or 'hold'
zoomEffectEnabled = true, -- Enable zoom effect when menu is shown
keys = {
toggle = 0x8AAA0AD4, -- LEFT ALT - to show/hide the menu
left = 0xD0842EDF, -- Wheel down (scroll)
right = 0xF78D7337, -- Wheel up (scroll)
select = 0xCEFD9220, -- E
back = 0x156F7119, -- BACKSPACE
favorite = 0x7F8D09B8, -- V - Remove favorite
help = 0x4CC0E2FE, -- B - Show help
modifier = 0x8FFC75D6, -- LEFT SHIFT - modifier for hotkey slots
},
slotAssignHoldTime = 1000, -- ms to hold number key to assign to slot
promptLabels = {
back = "Back",
switch = "Switch Mode",
favorite = "Remove Favorite",
help = "Help",
},
theme = 'default', -- Theme name from shared/themes.lua
}Theme System (shared/themes.lua) β
All visual and UI settings are defined in themes. Set Config.theme to any theme name to apply it.
To customize, either modify an existing theme directly in shared/themes.lua or create a new one.
Theme Structure β
Each theme contains:
['my_theme'] = {
fontId = 7, -- Font index (https://docs.bln-studio.com/docs/resources.html#text)
colors = {
itemBg = { r = 0, g = 0, b = 0, a = 200 }, -- Segment background
itemIcon = { r = 255, g = 255, b = 255, a = 255 }, -- Default icon color (per-item color overrides)
centerBg = { r = 0, g = 0, b = 0, a = 100 }, -- Center circle background
centerText = { r = 255, g = 255, b = 255, a = 255 }, -- Center text & icon color
progress = { r = 219, g = 2, b = 2, a = 255 }, -- Hold-to-assign progress bar
itemArrow = { r = 219, g = 2, b = 2, a = 255 }, -- Hover indicator overlay
header = {
bg = { r = 0, g = 0, b = 0, a = 200 }, -- Mode tabs background
text = { r = 255, g = 255, b = 255, a = 127 }, -- Inactive tab text
textHover = { r = 255, g = 255, b = 255, a = 255 }, -- Active/hovered tab text
},
onHover = {
itemBg = { r = 219, g = 2, b = 2, a = 255 }, -- Segment bg when hovered
itemIcon = { r = 255, g = 255, b = 255, a = 255 }, -- Icon color when hovered
centerText = { r = 219, g = 2, b = 2, a = 255 }, -- Center text when hovering item
}
}
}Available Themes β
| Theme | Description |
|---|---|
default | Classic dark with red accents |
western_sunset | Warm brown with orange highlights |
night_sky | Midnight blue with violet accents |
forest | Dark green with olive highlights |
desert_sand | Saddle brown with tan accents |
stealth | Dark gray with subtle silver |
royal | Deep blue with gold accents |
ember | Maroon with orange fire accents |
ocean_deep | Deep blue with teal highlights |
purple_dusk | Dark purple with lavender accents |
Applying a Theme β
In shared/config.lua:
Config = {
theme = 'western_sunset', -- Change this to any theme name
}Controls β
Keyboard & Mouse β
| Action | Key |
|---|---|
| Open/Close menu | LEFT ALT (hold or toggle) |
| Browse items | Scroll Wheel / Mouse movement |
| Select item | E |
| Go back | BACKSPACE |
| Switch mode | R |
| Remove favorite | V (hold) |
| Show help | B |
| Assign to hotkey slot | Hold 1-8 on a hovered item |
| Use hotkey (menu closed) | LEFT SHIFT + 1-8 |
Controller Support β
The menu works seamlessly with controllers. Configure controller keys in the keys config:
keys = {
toggle = 0x80F28E95, -- Controller L arrow
left = 0xC0651D40, -- R stick up
right = 0x8ED92E16, -- R stick down
select = 0xCEFD9220, -- E
back = 0x156F7119, -- BACKSPACE
},Menu Modes β
The wheel has three built-in modes, switchable with the R key:
- Main Menu - Your configured menu items
- Favorites - Up to 8 favorited items with assigned slots
- History - Recently used items, automatically tracked
Configure mode names and persistence in config:
modes = {
main = {
name = "Main menu",
icon = { dict = "toasts_mp_generic", name = "toast_mp_standalone_sp" }
},
favorites = {
name = "Favorites",
icon = { dict = "generic_textures", name = "star" },
persistent = true -- Save to local db (false = session only)
},
history = {
name = "History",
icon = { dict = "toasts_mp_generic", name = "mp_roles_collector_tier" },
persistent = false
}
}Favorites & Hotkey Slots β
Assigning Items to Slots β
- Open the wheel menu and hover over an item
- Hold a number key (1-8) until the progress bar fills
- The item is now assigned to that slot
Using Hotkeys β
With the menu closed, press LEFT SHIFT + Number (1-8) to instantly execute the assigned item.
Favorites Mode β
Switch to Favorites mode (R key) to see all assigned slots. Slot numbers are always visible. Use V (hold) to remove an item from favorites.
Adding Menu Items β
Items are defined in shared/menu.lua:
Menu.items = {
{
name = "item_unique_id",
label = "Item Name",
icon = {
texture_dict = "texture_dictionary",
texture_name = "texture_name"
},
color = { r = 255, g = 255, b = 255, a = 255 },
action = function()
-- Your code here
end
},
-- Item with submenu
{
name = "submenu_id",
label = "Submenu Name",
icon = { texture_dict = "...", texture_name = "..." },
color = { r = 255, g = 255, b = 255, a = 255 },
items = {
{
name = "sub_item_1",
label = "Submenu Item 1",
icon = { texture_dict = "...", texture_name = "..." },
color = { r = 255, g = 255, b = 255, a = 255 },
action = function()
-- Action for submenu item
end
},
}
}
}You can nest menus as deep as you want: menu > menu > menu > menu ...etc
Example With Nested Menus β
Menu.items = {
{
name = "clothing_menu",
label = "Clothes Menu",
icon = { texture_dict = "inventory_items_mp", texture_name = "clothing_generic_m_sweater" },
color = { r = 255, g = 255, b = 255, a = 255 },
items = {
{
name = "hat_menu",
label = "Hat",
icon = { texture_dict = "inventory_items_mp", texture_name = "clothing_generic_m_sweater" },
color = { r = 255, g = 255, b = 255, a = 255 },
items = {
-- Hat submenu items
}
},
}
},
{
name = "wagon_menu",
label = "Call My Wagon",
icon = { texture_dict = "inventory_items_mp", texture_name = "generic_coach" },
color = { r = 255, g = 255, b = 255, a = 255 },
items = {
{
name = "hunting_wagon",
label = "Hunting Wagon",
icon = { texture_dict = "inventory_items_mp", texture_name = "generic_coach" },
color = { r = 255, g = 255, b = 255, a = 255 },
action = function() print('Calling hunting wagon..') end
},
{
name = "delivery_wagon",
label = "Delivery Wagon",
icon = { texture_dict = "inventory_items_mp", texture_name = "generic_coach" },
color = { r = 255, g = 255, b = 255, a = 255 },
action = function() print('Calling delivery wagon..') end
}
}
}
}Filtering System β
Config.filter runs synchronously whenever the menu needs an itemβs state (it must return immediately; do not Wait or block inside the filter). It receives the item name (as defined in shared/menu.lua) and may use any logic: job, admin flags, game state, exports, etc.
If data arrives from the server later, store it in a client variable and read it in the filter; the next frame will pick up the new value. For the gap before data is loaded, return values that match how you want the menu to behave (for example, allow all items until the first sync, or the oppositeβyour choice).
filterMode (global default) β
filterMode = 'disable', -- 'disable' or 'hide'When you return only true or false, the second return is missing and the resource uses filterMode as the per-item mode for that decision:
filterMode | Effect when the filter disallows the item |
|---|---|
'disable' | Item stays visible with a lock; cannot run action or open a submenu, but the wedge is still there. |
'hide' | Item is not shown as a normal entry; it becomes a ghost wedge (faded background, no icon). |
For explicit per-item behavior, return a second value and ignore filterMode for that branch (see below).
Per-item return values β
Config.filter should return one or two values:
- First return:
true= item is allowed,false= item is not allowed (interpret with second return orfilterMode). - Second return (optional):
'disable'or'hide'to overridefilterModefor thatitemName.
| Returns | Result |
|---|---|
return true | Item enabled, normal. |
return false, 'disable' | Disabled (locked): visible, lock icon, no action / no submenu. |
return false, 'hide' | Hidden (ghost): not interactive; shows as a faded βemptyβ style wedge. |
If you return only one value, filterMode is used as the mode for a false result. If you return two values, the second overrides filterMode for that item.
filterHideItemOrder β
filterHideItemOrder = 'compact', -- 'in_place' or 'compact'This controls order on the wheel for items that resolve to ghost ('hide') in Main mode (root list and every submenu, while the mode is Main).
| Value | Behavior |
|---|---|
'compact'(default) | All non-ghost entries (visible + disabled/locked) first, in file order, then all ghost entries at the end of that list, in their original order among hiddens. |
'in_place' | Ghost wedges stay in the same order as in menu.lua (interleaved with visible items). |
Example β
Config = {
filterMode = 'disable', -- default when you only return true/false (no 2nd value)
filterHideItemOrder = 'in_place', -- or 'compact' for Main mode only (see above)
filter = function(itemName)
if itemName == "delivery_wagon" then
return false, 'disable' -- show locked, no action
end
if itemName == "admin_panel" and not IsPlayerAdmin() then
return false, 'hide' -- ghost wedge (or at end of list if compact)
end
return true
end,
}You can override the global filterMode per item by always returning a second parameter where needed, as in the example.
Interaction Modes β
Config = {
showMode = 'hold', -- 'toggle' or 'hold'
}toggle- Press the key once to open, press again to closehold- Hold the key to keep open, release to close
Exports β
-- Enable the menu
exports.bln_wheel:EnableMenu()
-- Disable the menu
exports.bln_wheel:DisableMenu()
-- Check if the menu is enabled
local isEnabled = exports.bln_wheel:IsMenuEnabled()
-- Check if the menu is currently visible
local isVisible = exports.bln_wheel:IsMenuVisible()
-- Force close the menu
exports.bln_wheel:CloseMenu()Custom Icons β
To use custom icons instead of game native icons, stream your own textures. See the custom textures tutorial for details.
Notification Settings β
Configure notification messages and placement in shared/config.lua:
notification = {
placement = "middle-right",
help_placement = "middle-right",
messages = {
['added_to_favorites'] = "Added to favorites: ~#ffcc00~%s~e~",
['removed_from_favorites'] = "Removed from favorites: ~#ffcc00~%s~e~",
['favorites_limit_reached'] = "Favorites limit reached!",
['assigned_to_slot'] = "Assigned ~#ffcc00~%s~e~ to slot %d",
['slot_replaced'] = "Replaced slot %d with ~#ffcc00~%s~e~",
['slot_empty'] = "Favorite slot %d is empty",
['executed_from_slot'] = "~#ffcc00~%s~e~",
['cannot_assign'] = "Cannot assign this item to favorites",
['cannot_assign_menu'] = "Navigate into this menu and select an item to assign",
['help_title'] = "~#ffcc00~Wheel Menu~e~",
['help'] = "Hold ~key:1~ - ~key:8~ to assign item to a hotkey slot. Use ~key:SHIFT~ + number outside menu to quick use.",
}
}Support β
Need help? Join our Discord server for support and updates!
- Discord: Join Here
- Live support and community help
- Regular updates and improvements
