Início › Documentação › Tarefas e melhorias · origen_ilegalv2

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.


Conceitos

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:

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

Momento de registo

O OrgPanels carrega os seus catálogos quando o recurso arranca. Regista sempre dentro de onResourceStart para garantir que o módulo está pronto:

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


Registar tarefas

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' },
})
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.


Registar melhorias

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
    },
})
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

Várias tarefas obrigatórias

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


Concluir tarefas a partir do teu recurso

Chama-o sempre que um membro da gangue terminar a atividade correspondente dentro do jogo:

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

Valor devolvido

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

Verificar se uma tarefa está concluída

IsGangTaskCompleted(gangId, taskKey) → boolean

Verificação síncrona em memória. Usa-a dentro de callbacks do servidor antes de permitir uma ação:

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)

Desbloquear melhorias

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:

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

Valor devolvido

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'

Restringir conteúdo com feature keys

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.

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)

Ler o estado completo

GetGangTasks(gangId) → table[]

Devolve o catálogo completo enriquecido com o progresso de cada gangue:

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[]

Devolve o catálogo completo de melhorias com o estado de desbloqueio e a resolução de dependências:

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

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.

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)

Dispara exatamente uma vez por cada par (gangId, upgradeKey), já que as melhorias são permanentes.

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)

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.

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

Exemplo de integração completo

Segue-se uma versão resumida do aspeto de uma integração completa a partir de um recurso externo:

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)