OrigenNetwork
Docs

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

luaconfig/interactions.lua
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

FieldTypeRequiredDescription
idstringyesUnique action id. Used to route the execution server-side.
ordernumberyesSort order in the menu (ascending).
labelstringyesButton text. Can be a plain string or a locale key (e.g. quickmenu.actions.myaction.label).
iconstringyesFont Awesome class (e.g. fa-solid fa-magnifying-glass).
requiredPermissionstring | nilyesRank permission the player needs. nil = any gang member.
canExecute(playerPed, targetPed)functionnoClient-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)functionyesServer-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:

  1. Client: the action is hidden from the menu if HasPermission(action.requiredPermission) returns false.
  2. Server: Gang:hasPermission(identifier, action.requiredPermission) is checked again inside origen_gang:server:quickAction before running onExecute. 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).
luaconfig/gangs.lua — DefaultRanks
{
    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

luaconfig/interactions.lua — Actions
{
    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

luaconfig/interactions.lua — Actions
{
    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

text
[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):

lua
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:

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:

luaserver/custom/qa_revive.lua
QuickActions = QuickActions or {}
 
function QuickActions.Revive(src, targetId)
    TriggerClientEvent('hospital:client:Revive', targetId)
end
luaconfig/interactions.lua
onExecute = function(src, targetId)
    QuickActions.Revive(src, targetId)
end,