OrigenNetwork
Docs

Tasks & Upgrades · origen_ilegalv2

The Organization Panel (OrgPanels) lets you wire any external resource — missions, heists, robberies — into the gang's progress board. Your resource registers tasks and upgrades; origen_ilegalv2 handles persistence, validation, NUI display, and hooks.


Concepts

ConceptWhat it is
TaskA milestone a gang can complete. Tracks completed and times_completed. Can be one-off or repeatable.
UpgradeA permanent unlock for a gang. Requires one or more tasks to be completed before it can be activated.
Feature keyA string stored in effect.feature on an upgrade. External resources use this key — not the upgrade_key — to check access. Decouples your resource from upgrade names.

Data flow:

text
External resource registers task/upgrade on boot

Gang members complete tasks in-game → your resource calls CompleteGangTask

Leader unlocks upgrade from the gang panel (NUI) or your code calls UnlockGangUpgrade

Other resources gate content behind IsGangFeatureUnlocked

Timing — when to register

OrgPanels loads its catalogs when the resource starts. Always register inside onResourceStart to guarantee the module is ready:

lua
AddEventHandler('onResourceStart', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
 
    exports['origen_ilegalv2']:AddOrganizationTask({ ... })
    exports['origen_ilegalv2']:AddOrganizationUpgrade({ ... })
end)

Calling these exports at the top level (outside an event) risks a race condition where origen_ilegalv2 has not finished loading yet.


Registering tasks

AddOrganizationTask(taskDef)

lua
exports['origen_ilegalv2']:AddOrganizationTask({
    task_key      = 'task_recon_north',           -- unique key (snake_case recommended)
    label         = 'North Recon',                -- displayed in the gang panel
    description   = 'Perform recon of the northern zone.',
    category      = 'heist',                      -- free-form category for NUI grouping
    is_repeatable = false,                        -- true = can be completed multiple times
    meta          = { zone = 'north', reward_hint = 'Unlocks bank heist' },
})
FieldTypeRequiredDescription
task_keystringUnique identifier. Used in required_tasks of upgrades and in all export calls
labelstringDisplay name shown in the gang panel
descriptionstringLonger description shown in the NUI
categorystringFree-form label used to group tasks in the panel
is_repeatablebooleantrue allows the task to be completed more than once (each call increments times_completed). Default: false
metatableArbitrary data stored as JSON. Readable by hooks and GetGangTasks. Not used by the core system
enabledbooleanSet to false to hide the task from the panel without deleting it. Default: true

If task_key already exists in the database, the row is updated (upsert). Safe to call on every boot.


Registering upgrades

AddOrganizationUpgrade(upgradeDef)

lua
exports['origen_ilegalv2']:AddOrganizationUpgrade({
    upgrade_key    = 'upgrade_robbery_tier2',
    label          = 'Access: Medium Heists',
    description    = 'Enables access to medium-tier heist jobs.',
    type           = 'robbery_unlock',
    required_tasks = { 'task_recon_north' },   -- all must be completed before unlocking
    effect         = {
        feature  = 'robberies:tier2',          -- key your resource checks
        min_cops = 4,                          -- optional extra data
    },
})
FieldTypeRequiredDescription
upgrade_keystringUnique identifier
labelstringDisplay name in the gang panel
descriptionstringLonger description
typestringFree-form type label for NUI grouping (e.g. 'robbery_unlock', 'logistics', 'economy')
required_taskstable or stringArray of task_key values that must be completed. Also accepts a CSV string: 'task_a, task_b'
effecttableArbitrary data. effect.feature is the key other resources query via IsGangFeatureUnlocked
enabledbooleanDefault: true

Multiple required tasks

lua
required_tasks = {
    'task_recon_north',
    'task_supply_route',
    'task_eliminate_rivals',
},

All listed tasks must be completed before the upgrade can be unlocked. The NUI shows per-task completion status to the gang.


Completing tasks from your resource

Call this whenever a gang member finishes the corresponding in-game activity:

lua
local function OnReconMissionComplete(source)
    local gang = exports['origen_ilegalv2']:GetPlayerGangData(source)
    if not gang then return end
 
    local result = exports['origen_ilegalv2']:CompleteGangTask(
        gang.id,
        'task_recon_north',
        {
            completed_by = GetPlayerIdentifier(source),   -- optional
            progress     = { duration_seconds = 320 },   -- optional meta, stored in DB
        }
    )
 
    if result.ok then
        print('Task completed. Times:', result.times_completed)
    elseif result.error == 'already_completed' then
        -- Non-repeatable task was already done — not an error, just a no-op
    end
end

Return value

FieldTypeDescription
okbooleantrue on success
errorstring|nil'already_completed' / 'task_not_found' / 'invalid_gang'
times_completednumberHow many times completed after this call

Checking task completion

IsGangTaskCompleted(gangId, taskKey)boolean

Synchronous in-memory check. Use this inside server callbacks before allowing an action:

lua
lib.callback.register('myresource:server:canStartHeist', function(source)
    local gang = exports['origen_ilegalv2']:GetPlayerGangData(source)
    if not gang then return false end
 
    return exports['origen_ilegalv2']:IsGangTaskCompleted(gang.id, 'task_recon_north')
end)

Unlocking upgrades

Upgrades are normally unlocked by the gang leader or a member with the manage_org_upgrades rank permission from the NUI. You can also unlock them from code — useful for automatic rewards or server events:

lua
local result = exports['origen_ilegalv2']:UnlockGangUpgrade(
    gangId,
    'upgrade_robbery_tier2',
    {
        unlocked_by = 'system_auto',         -- optional, stored in DB
        meta        = { source = 'event' },  -- optional
    }
)
 
if result.ok then
    -- success
elseif result.error == 'already_unlocked' then
    -- idempotent, not a real error
elseif result.error == 'missing_dependencies' then
    for _, t in ipairs(result.missing_tasks or {}) do
        print('Missing task:', t.task_key, t.label)
    end
end

Return value

FieldTypeDescription
okbooleantrue on success
errorstring|nil'already_unlocked' / 'missing_dependencies' / 'upgrade_not_found' / 'invalid_gang'
missing_tasks{ task_key, label }[]Only present when error == 'missing_dependencies'

Gating content with feature keys

This is the recommended pattern. Your resource does not know the upgrade_key — only the feature string. You can rename the upgrade later without touching other resources.

lua
-- In origen_robberies or any external resource:
lib.callback.register('origen_robberies:server:getAvailable', function(source)
    local gangId = exports['origen_ilegalv2']:GetPlayerGang(source)
    if not gangId then return {} end
 
    local hasTier2 = exports['origen_ilegalv2']:IsGangFeatureUnlocked(gangId, 'robberies:tier2')
    local hasTier3 = exports['origen_ilegalv2']:IsGangFeatureUnlocked(gangId, 'robberies:tier3')
 
    -- return only the heists the gang has unlocked
end)

Reading full state

GetGangTasks(gangId)table[]

Returns the full catalog enriched with per-gang progress:

lua
local tasks = exports['origen_ilegalv2']:GetGangTasks(gang.id)
-- tasks[i] = {
--   task_key, label, description, category,
--   is_repeatable, meta,
--   completed,        -- boolean
--   times_completed,  -- number
--   completed_at,     -- datetime or nil
--   last_completed_at -- datetime or nil
-- }

GetGangUpgrades(gangId)table[]

Returns the full upgrade catalog with unlock state and dependency resolution:

lua
local upgrades = exports['origen_ilegalv2']:GetGangUpgrades(gang.id)
-- upgrades[i] = {
--   upgrade_key, label, description, type,
--   required_tasks,   -- [{ task_key, label, completed }]
--   deps_satisfied,   -- boolean: all required tasks done
--   unlocked,         -- boolean
-- }

Hooks

The hooks file lives at custom/server/org_panels_hooks.lua. It is loaded by the resource and executed inside origen_ilegalv2 — no cross-resource wiring needed.

Edit only the body of each function. Do not rename the functions or the OrgPanelsHooks table.

OrgPanelsHooks.onTaskCompleted(ctx)

Fires every time a task is marked completed for a gang, including repeatable ones.

luacustom/server/org_panels_hooks.lua
function OrgPanelsHooks.onTaskCompleted(ctx)
    -- ctx.gangId         → gang ID
    -- ctx.taskKey        → task key
    -- ctx.taskDef        → full task definition (label, category, is_repeatable, meta)
    -- ctx.timesCompleted → total completions after this call
    -- ctx.identifier     → citizenid of who completed it (nil if automatic)
    -- ctx.gangInstance   → Gang object (members, ranks, addMoney, addLog, etc.)
 
    -- Reward all online members
    if ctx.gangInstance then
        for identifier, _ in pairs(ctx.gangInstance.members) do
            local src = GetPlayerByIdentifier(identifier)
            if src then
                NotifyPlayer(src, 'Task completed: ' .. ctx.taskDef.label, 'success')
            end
        end
    end
 
    -- Reward the member who completed it
    if ctx.identifier then
        local src = GetPlayerByIdentifier(ctx.identifier)
        if src then
            AddPlayerMoney(src, 'black_money', 5000)
        end
    end
end

OrgPanelsHooks.onUpgradeUnlocked(ctx)

Fires exactly once per (gangId, upgradeKey) pair — upgrades are permanent.

lua
function OrgPanelsHooks.onUpgradeUnlocked(ctx)
    -- ctx.gangId       → gang ID
    -- ctx.upgradeKey   → upgrade key
    -- ctx.upgradeDef   → full definition (label, type, required_tasks, effect)
    -- ctx.identifier   → who unlocked it (nil if automatic)
    -- ctx.gangInstance → Gang object
 
    -- Notify all gang members
    if ctx.gangInstance then
        for identifier, _ in pairs(ctx.gangInstance.members) do
            local src = GetPlayerByIdentifier(identifier)
            if src then
                TriggerClientEvent('myresource:client:upgradeUnlocked', src, {
                    upgradeKey = ctx.upgradeKey,
                    label      = ctx.upgradeDef.label,
                    effect     = ctx.upgradeDef.effect,
                })
            end
        end
    end
 
    -- Branch by type
    if ctx.upgradeDef.type == 'robbery_unlock' then
        -- specific logic for heist unlocks
    end
end

OrgPanelsHooks.onUpgradeUnlockBlocked(ctx)

Fires when someone attempts to unlock an upgrade but required tasks are not yet complete. Useful for logging or giving extra feedback.

lua
function OrgPanelsHooks.onUpgradeUnlockBlocked(ctx)
    -- ctx.gangId       → gang ID
    -- ctx.upgradeKey   → upgrade that was attempted
    -- ctx.upgradeDef   → full upgrade definition
    -- ctx.missingTasks → [{ task_key, label }] list of incomplete tasks
    -- ctx.identifier   → who attempted (nil if automatic)
    -- ctx.source       → player source if the attempt came from the NUI
 
    if ctx.source then
        local missing = {}
        for _, t in ipairs(ctx.missingTasks) do
            missing[#missing + 1] = t.label
        end
        NotifyPlayer(ctx.source, 'Complete first: ' .. table.concat(missing, ', '), 'error')
    end
end

Complete integration example

Below is a condensed version of what a full integration from an external resource looks like:

luamy_missions/server/main.lua
-- 1. Register on boot
AddEventHandler('onResourceStart', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
 
    exports['origen_ilegalv2']:AddOrganizationTask({
        task_key     = 'mission_convoy_00',
        label        = 'Intercept Convoy',
        description  = 'Ambush and rob the supply convoy.',
        category     = 'mission',
        is_repeatable = false,
    })
 
    exports['origen_ilegalv2']:AddOrganizationUpgrade({
        upgrade_key    = 'unlock_convoy_hard',
        label          = 'Hard Convoys',
        description    = 'Unlocks armored convoy missions.',
        type           = 'mission_unlock',
        required_tasks = { 'mission_convoy_00' },
        effect         = { feature = 'missions:convoy_hard' },
    })
end)
 
-- 2. Complete the task when mission ends
local function OnConvoySuccess(source)
    local gang = exports['origen_ilegalv2']:GetPlayerGangData(source)
    if not gang then return end
 
    exports['origen_ilegalv2']:CompleteGangTask(gang.id, 'mission_convoy_00', {
        completed_by = GetPlayerIdentifier(source),
    })
end
 
-- 3. Gate hard missions behind the feature
lib.callback.register('my_missions:server:getMissions', function(source)
    local gangId = exports['origen_ilegalv2']:GetPlayerGang(source)
    local hasHard = gangId and exports['origen_ilegalv2']:IsGangFeatureUnlocked(gangId, 'missions:convoy_hard')
 
    local missions = { { id = 'convoy_easy', label = 'Supply Convoy (Easy)' } }
    if hasHard then
        missions[#missions + 1] = { id = 'convoy_hard', label = 'Armored Convoy (Hard)' }
    end
    return missions
end)