Home › Documentation › 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.
| Concept | What it is |
|---|---|
| Task | A milestone a gang can complete. Tracks completed and times_completed. Can be one-off or repeatable. |
| Upgrade | A permanent unlock for a gang. Requires one or more tasks to be completed before it can be activated. |
| Feature key | A 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:
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 IsGangFeatureUnlockedOrgPanels loads its catalogs when the resource starts. Always register inside onResourceStart to guarantee the module is ready:
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.
AddOrganizationTask(taskDef)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' },
})| Field | Type | Required | Description |
|---|---|---|---|
task_key | string | ✅ | Unique identifier. Used in required_tasks of upgrades and in all export calls |
label | string | ✅ | Display name shown in the gang panel |
description | string | — | Longer description shown in the NUI |
category | string | — | Free-form label used to group tasks in the panel |
is_repeatable | boolean | — | true allows the task to be completed more than once (each call increments times_completed). Default: false |
meta | table | — | Arbitrary data stored as JSON. Readable by hooks and GetGangTasks. Not used by the core system |
enabled | boolean | — | Set 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.
AddOrganizationUpgrade(upgradeDef)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
},
})| Field | Type | Required | Description |
|---|---|---|---|
upgrade_key | string | ✅ | Unique identifier |
label | string | ✅ | Display name in the gang panel |
description | string | — | Longer description |
type | string | — | Free-form type label for NUI grouping (e.g. 'robbery_unlock', 'logistics', 'economy') |
required_tasks | table or string | — | Array of task_key values that must be completed. Also accepts a CSV string: 'task_a, task_b' |
effect | table | — | Arbitrary data. effect.feature is the key other resources query via IsGangFeatureUnlocked |
enabled | boolean | — | Default: true |
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.
Call this whenever a gang member finishes the corresponding in-game activity:
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| Field | Type | Description |
|---|---|---|
ok | boolean | true on success |
error | string|nil | 'already_completed' / 'task_not_found' / 'invalid_gang' |
times_completed | number | How many times completed after this call |
IsGangTaskCompleted(gangId, taskKey) → booleanSynchronous in-memory check. Use this inside server callbacks before allowing an action:
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)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:
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| Field | Type | Description |
|---|---|---|
ok | boolean | true on success |
error | string|nil | 'already_unlocked' / 'missing_dependencies' / 'upgrade_not_found' / 'invalid_gang' |
missing_tasks | { task_key, label }[] | Only present when error == 'missing_dependencies' |
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.
-- 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)GetGangTasks(gangId) → table[]Returns the full catalog enriched with per-gang progress:
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:
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
-- }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.
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
endOrgPanelsHooks.onUpgradeUnlocked(ctx)Fires exactly once per (gangId, upgradeKey) pair — upgrades are permanent.
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
endOrgPanelsHooks.onUpgradeUnlockBlocked(ctx)Fires when someone attempts to unlock an upgrade but required tasks are not yet complete. Useful for logging or giving extra feedback.
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
endBelow is a condensed version of what a full integration from an external resource looks like:
-- 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)