Início › Documentação › Tarefas e melhorias · origen_ilegalv2
O Painel da organização (OrgPanels) permite-te ligar qualquer recurso externo (missões, assaltos, roubos) ao quadro de progresso da gangue. O teu recurso regista tarefas e melhorias; o origen_ilegalv2 trata da persistência, da validação, da apresentação na NUI e dos hooks.
| Conceito | O que é |
|---|---|
| Tarefa | Um marco que uma gangue pode concluir. Regista completed e times_completed. Pode ser única ou repetível. |
| Melhoria | Um desbloqueio permanente para uma gangue. Exige a conclusão de uma ou mais tarefas antes de poder ser ativada. |
| Feature key | Um string guardado em effect.feature de uma melhoria. Os recursos externos usam esta chave (e não a upgrade_key) para verificar o acesso. Desacopla o teu recurso dos nomes das melhorias. |
Fluxo de dados:
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 IsGangFeatureUnlockedO OrgPanels carrega os seus catálogos quando o recurso arranca. Regista sempre dentro de onResourceStart para garantir que o módulo está pronto:
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
exports['origen_ilegalv2']:AddOrganizationTask({ ... })
exports['origen_ilegalv2']:AddOrganizationUpgrade({ ... })
end)Chamar estes exports ao nível superior (fora de um evento) pode causar uma condição de corrida se o origen_ilegalv2 ainda não tiver acabado de carregar.
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' },
})| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
task_key |
string | ✅ | Identificador único. Usa-se em required_tasks das melhorias e em todas as chamadas a exports |
label |
string | ✅ | Nome visível no painel da gangue |
description |
string | — | Descrição mais longa, mostrada na NUI |
category |
string | — | Etiqueta livre usada para agrupar as tarefas no painel |
is_repeatable |
boolean | — | Com true, a tarefa pode ser concluída mais de uma vez (cada chamada incrementa times_completed). Por omissão: false |
meta |
table | — | Dados arbitrários guardados como JSON. Legíveis a partir dos hooks e de GetGangTasks. O sistema central não os usa |
enabled |
boolean | — | Define-o como false para esconder a tarefa do painel sem a apagar. Por omissão: true |
Se task_key já existir na base de dados, a linha é atualizada (upsert). É seguro chamá-lo em cada arranque.
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
},
})| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
upgrade_key |
string | ✅ | Identificador único |
label |
string | ✅ | Nome visível no painel da gangue |
description |
string | — | Descrição mais longa |
type |
string | — | Etiqueta de tipo livre para agrupar na NUI (p. ex. 'robbery_unlock', 'logistics', 'economy') |
required_tasks |
table ou string | — | Array de valores task_key que têm de estar concluídos. Aceita também um string CSV: 'task_a, task_b' |
effect |
table | — | Dados arbitrários. effect.feature é a chave que outros recursos consultam através de IsGangFeatureUnlocked |
enabled |
boolean | — | Por omissão: true |
required_tasks = {
'task_recon_north',
'task_supply_route',
'task_eliminate_rivals',
},Todas as tarefas indicadas têm de estar concluídas antes de a melhoria poder ser desbloqueada. A NUI mostra à gangue o estado de cada tarefa.
Chama-o sempre que um membro da gangue terminar a atividade correspondente dentro do jogo:
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| Campo | Tipo | Descrição |
|---|---|---|
ok |
boolean | true se correu bem |
error |
string|nil | 'already_completed' / 'task_not_found' / 'invalid_gang' |
times_completed |
number | Quantas vezes foi concluída após esta chamada |
IsGangTaskCompleted(gangId, taskKey) → booleanVerificação síncrona em memória. Usa-a dentro de callbacks do servidor antes de permitir uma ação:
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)Normalmente as melhorias são desbloqueadas pelo líder da gangue, ou por um membro com a permissão de grau manage_org_upgrades, a partir da NUI. Também as podes desbloquear por código, o que é útil para recompensas automáticas ou eventos do servidor:
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| Campo | Tipo | Descrição |
|---|---|---|
ok |
boolean | true se correu bem |
error |
string|nil | 'already_unlocked' / 'missing_dependencies' / 'upgrade_not_found' / 'invalid_gang' |
missing_tasks |
{ task_key, label }[] |
Só presente quando error == 'missing_dependencies' |
Este é o padrão recomendado. O teu recurso não conhece a upgrade_key, apenas o string feature. Mais tarde podes mudar o nome da melhoria sem mexer noutros recursos.
-- 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[]Devolve o catálogo completo enriquecido com o progresso de cada gangue:
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[]Devolve o catálogo completo de melhorias com o estado de desbloqueio e a resolução de dependências:
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
-- }O ficheiro de hooks está em custom/server/org_panels_hooks.lua. O recurso carrega-o e executa-o dentro do origen_ilegalv2, sem necessidade de ligações entre recursos.
Edita apenas o corpo de cada função. Não mudes o nome das funções nem da tabela OrgPanelsHooks.
OrgPanelsHooks.onTaskCompleted(ctx)Dispara sempre que uma tarefa é marcada como concluída para uma gangue, incluindo as repetíveis.
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)Dispara exatamente uma vez por cada par (gangId, upgradeKey), já que as melhorias são permanentes.
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)Dispara quando alguém tenta desbloquear uma melhoria mas as tarefas necessárias ainda não estão concluídas. Útil para registar logs ou dar informação adicional.
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
endSegue-se uma versão resumida do aspeto de uma integração completa a partir de um recurso externo:
-- 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)