Inicio › Documentación › Tareas y mejoras · origen_ilegalv2

Tareas y mejoras · origen_ilegalv2

El Panel de organización (OrgPanels) te permite conectar cualquier recurso externo (misiones, atracos, robos) al tablero de progreso de la banda. Tu recurso registra tareas y mejoras; origen_ilegalv2 se encarga de la persistencia, la validación, la visualización en la NUI y los hooks.


Conceptos

Concepto Qué es
Tarea Un hito que una banda puede completar. Registra completed y times_completed. Puede ser única o repetible.
Mejora Un desbloqueo permanente para una banda. Requiere completar una o varias tareas antes de poder activarse.
Feature key Un string guardado en effect.feature de una mejora. Los recursos externos usan esta clave (no la upgrade_key) para comprobar el acceso. Desacopla tu recurso de los nombres de las mejoras.

Flujo de datos:

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 registro

OrgPanels carga sus catálogos cuando arranca el recurso. Registra siempre dentro de onResourceStart para garantizar que el módulo está listo:

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

Llamar a estos exports a nivel superior (fuera de un evento) puede provocar una condición de carrera si origen_ilegalv2 aún no ha terminado de cargar.


Registrar tareas

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 Obligatorio Descripción
task_key string ✅ Identificador único. Se usa en required_tasks de las mejoras y en todas las llamadas a exports
label string ✅ Nombre visible en el panel de la banda
description string — Descripción más larga, mostrada en la NUI
category string — Etiqueta libre usada para agrupar las tareas en el panel
is_repeatable boolean — Con true, la tarea puede completarse más de una vez (cada llamada incrementa times_completed). Por defecto: false
meta table — Datos arbitrarios guardados como JSON. Legibles desde los hooks y GetGangTasks. El sistema central no los usa
enabled boolean — Ponlo en false para ocultar la tarea del panel sin borrarla. Por defecto: true

Si task_key ya existe en la base de datos, la fila se actualiza (upsert). Es seguro llamarla en cada arranque.


Registrar mejoras

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 Obligatorio Descripción
upgrade_key string ✅ Identificador único
label string ✅ Nombre visible en el panel de la banda
description string — Descripción más larga
type string — Etiqueta de tipo libre para agrupar en la NUI (p. ej. 'robbery_unlock', 'logistics', 'economy')
required_tasks table o string — Array de valores task_key que deben estar completados. También acepta un string CSV: 'task_a, task_b'
effect table — Datos arbitrarios. effect.feature es la clave que otros recursos consultan mediante IsGangFeatureUnlocked
enabled boolean — Por defecto: true

Varias tareas obligatorias

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

Todas las tareas indicadas deben estar completadas antes de poder desbloquear la mejora. La NUI muestra a la banda el estado de cada tarea.


Completar tareas desde tu recurso

Llámalo cada vez que un miembro de la banda termine la actividad correspondiente dentro del juego:

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 devuelto

Campo Tipo Descripción
ok boolean true si todo ha ido bien
error string|nil 'already_completed' / 'task_not_found' / 'invalid_gang'
times_completed number Cuántas veces se ha completado tras esta llamada

Comprobar si una tarea está completada

IsGangTaskCompleted(gangId, taskKey) → boolean

Comprobación síncrona en memoria. Úsala dentro de callbacks del servidor antes de permitir una acción:

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 mejoras

Normalmente las mejoras las desbloquea el líder de la banda, o un miembro con el permiso de rango manage_org_upgrades, desde la NUI. También puedes desbloquearlas desde código, algo útil para recompensas automáticas o eventos del 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 devuelto

Campo Tipo Descripción
ok boolean true si todo ha ido bien
error string|nil 'already_unlocked' / 'missing_dependencies' / 'upgrade_not_found' / 'invalid_gang'
missing_tasks { task_key, label }[] Solo presente cuando error == 'missing_dependencies'

Restringir contenido con feature keys

Este es el patrón recomendado. Tu recurso no conoce la upgrade_key, solo el string feature. Más adelante puedes renombrar la mejora sin tocar otros 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)

Leer el estado completo

GetGangTasks(gangId) → table[]

Devuelve el catálogo completo enriquecido con el progreso de cada banda:

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

Devuelve el catálogo completo de mejoras con el estado de desbloqueo y la resolución de dependencias:

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

El fichero de hooks está en custom/server/org_panels_hooks.lua. El recurso lo carga y lo ejecuta dentro de origen_ilegalv2, sin necesidad de conexiones entre recursos.

Edita solo el cuerpo de cada función. No cambies el nombre de las funciones ni de la tabla OrgPanelsHooks.

OrgPanelsHooks.onTaskCompleted(ctx)

Se dispara cada vez que una tarea se marca como completada para una banda, incluidas las repetibles.

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)

Se dispara exactamente una vez por cada par (gangId, upgradeKey), ya que las mejoras son 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)

Se dispara cuando alguien intenta desbloquear una mejora pero las tareas necesarias aún no están completadas. Útil para registrar logs o dar información 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

Ejemplo de integración completo

A continuación tienes una versión resumida de cómo queda una integración completa desde un 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)