Custom Quick Menu Actions · origen_ilegalv2
Config.QuickMenu.Actions defines the entries shown in the F6 menu when the player is close to another player. Each action runs server-side through a handler you declare inline.
You can add your own actions without touching core code — just append entries to the array.
Anatomy of an action
Config.QuickMenu = {
Enable = true,
Key = 'F6',
UseCommand = true,
CommandName = 'quickmenu',
canInteract = function(playerPed, targetPed)
return true, nil
end,
Actions = {
{
id = 'search',
order = 1,
label = 'quickmenu.actions.search.label',
icon = 'fa-solid fa-magnifying-glass',
requiredPermission = 'quick_search',
canExecute = function(playerPed, targetPed)
return true, nil
end,
onExecute = function(src, targetId)
QuickActions.Search(src, targetId)
end,
},
},
}Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique action id. Used to route the execution server-side. |
order | number | yes | Sort order in the menu (ascending). |
label | string | yes | Button text. Can be a plain string or a locale key (e.g. quickmenu.actions.myaction.label). |
icon | string | yes | Font Awesome class (e.g. fa-solid fa-magnifying-glass). |
requiredPermission | string | nil | yes | Rank permission the player needs. nil = any gang member. |
canExecute(playerPed, targetPed) | function | no | Client-side gate called before sending the request to the server. Return true to allow, or false, reason to block with a notification. |
onExecute(src, targetId) | function | yes | Server-side handler. src is the caller, targetId is the closest player within 3m. |
The core already validates distance (3m), permission, and whether a target exists before calling onExecute. You only need to implement the actual behavior.
Permission system
Every action that declares requiredPermission is gated twice:
- Client: the action is hidden from the menu if
HasPermission(action.requiredPermission)returns false. - Server:
Gang:hasPermission(identifier, action.requiredPermission)is checked again insideorigen_gang:server:quickActionbefore runningonExecute. Anti-tamper by design.
Adding your own permission flag
Declare the flag on every rank in Config.Gang.DefaultRanks (config/gangs.lua). Any flag you list there is automatically:
- Enforced by
Gang:hasPermission(). - Editable at runtime from the gang panel (for ranks with
manage_ranks).
{
id = 1, name = "Leader",
permissions = {
-- ... existing flags ...
quick_revive = true, -- 👈 new flag
},
},
{
id = 3, name = "Member",
permissions = {
quick_revive = false,
},
},Ranks without the flag explicitly set will NOT see or execute the action. Missing flags default to false.
Example 1 — revive a downed teammate
{
id = 'revive',
order = 6,
label = 'quickmenu.actions.revive.label',
icon = 'fa-solid fa-heart-pulse',
requiredPermission = 'quick_revive',
-- Client gate: only show/allow if the target is dead
canExecute = function(playerPed, targetPed)
if not IsPedDeadOrDying(targetPed, true) then
return false, 'target_not_dead'
end
return true, nil
end,
-- Server handler
onExecute = function(src, targetId)
TriggerClientEvent('hospital:client:Revive', targetId)
-- Optional: charge an item, log the action, etc.
end,
},Example 2 — give gang cash to a nearby member
{
id = 'give_cash',
order = 7,
label = 'quickmenu.actions.give_cash.label',
icon = 'fa-solid fa-hand-holding-dollar',
requiredPermission = 'withdraw', -- reuses the existing bank permission
canExecute = function(playerPed, targetPed)
if #(GetEntityCoords(playerPed) - GetEntityCoords(targetPed)) > 2.0 then
return false, 'too_far'
end
return true, nil
end,
onExecute = function(src, targetId)
local gang = Gang.GetPlayerGangBySource(src)
if not gang then return end
local amount = 500
if gang.bank < amount then
NotifyPlayer(src, 'Not enough funds', 'error')
return
end
gang.bank = gang.bank - amount
-- give to target via your framework API
exports.ox_inventory:AddItem(targetId, 'money', amount)
gang:addLog(GetPlayerIdentifier(src), 'quick_give_cash', {
amount = amount,
target = GetPlayerName(targetId),
})
end,
},Execution flow
[F6] ─▶ OpenQuickMenu()
│
├─▶ GetAvailableActions() (client-side permission filter)
│
└─▶ player picks action
│
├─▶ canExecute() (client gate, optional)
│
└─▶ TriggerServerEvent('origen_gang:server:quickAction', id, targetId)
│
├─▶ validate Config.Actions[id] exists
├─▶ validate gang:hasPermission(requiredPermission)
├─▶ validate target is within 3m (lib.getClosestPlayer)
│
└─▶ action.onExecute(src, targetId)
│
└─▶ TriggerClientEvent('origen_gang:client:quickActionExecuted', targetId, id, src)The quickActionExecuted client event fires on the target — you can listen to it if your custom action needs the victim to play an animation, show a notification, etc.
Global gate — canInteract
Config.QuickMenu.canInteract(playerPed, targetPed) runs before any action is listed. Use it to block the whole menu based on custom rules (e.g. target is inside a vehicle, or in a safe zone):
canInteract = function(playerPed, targetPed)
if IsPedInAnyVehicle(targetPed, false) then
return false, 'target_in_vehicle'
end
return true, nil
end,Returning false hides every per-target action for that interaction. The ZoneSale toggle and other non-target entries are unaffected.
Extending QuickActions (optional)
The built-in helpers live in server/functions/f_quick_actions.lua:
QuickActions.Search(src, targetId)
QuickActions.Handcuff(src, targetId)
QuickActions.Escort(src, targetId)
QuickActions.PutInVehicle(src, targetId)
QuickActions.TakeOutVehicle(src, targetId)If you want to keep your logic tidy you can add your own function to the QuickActions table from a companion file and just call it from onExecute:
QuickActions = QuickActions or {}
function QuickActions.Revive(src, targetId)
TriggerClientEvent('hospital:client:Revive', targetId)
endonExecute = function(src, targetId)
QuickActions.Revive(src, targetId)
end,