Accueil › Documentation › Tâches et améliorations · origen_ilegalv2

Tâches et améliorations · origen_ilegalv2

Le Panneau d’organisation (OrgPanels) te permet de brancher n’importe quelle ressource externe (missions, braquages, vols) sur le tableau de progression du gang. Ta ressource enregistre des tâches et des améliorations ; origen_ilegalv2 gère la persistance, la validation, l’affichage NUI et les hooks.


Concepts

Concept Ce que c’est
Tâche Un jalon qu’un gang peut accomplir. Suit completed et times_completed. Peut être unique ou répétable.
Amélioration Un déblocage permanent pour un gang. Nécessite d’accomplir une ou plusieurs tâches avant de pouvoir être activée.
Feature key Un string stocké dans effect.feature d’une amélioration. Les ressources externes utilisent cette clé (et non la upgrade_key) pour vérifier l’accès. Elle découple ta ressource des noms des améliorations.

Flux de données :

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 — quand enregistrer

OrgPanels charge ses catalogues au démarrage de la ressource. Enregistre toujours dans onResourceStart pour garantir que le module est prêt :

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

Appeler ces exports au niveau supérieur (en dehors d’un événement) risque de provoquer une condition de concurrence si origen_ilegalv2 n’a pas encore fini de charger.


Enregistrer des tâches

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' },
})
Champ Type Obligatoire Description
task_key string ✅ Identifiant unique. Utilisé dans required_tasks des améliorations et dans tous les appels d’exports
label string ✅ Nom affiché dans le panneau du gang
description string — Description plus longue, affichée dans la NUI
category string — Libellé libre servant à regrouper les tâches dans le panneau
is_repeatable boolean — Avec true, la tâche peut être accomplie plusieurs fois (chaque appel incrémente times_completed). Par défaut : false
meta table — Données arbitraires stockées en JSON. Lisibles par les hooks et GetGangTasks. Non utilisées par le système central
enabled boolean — Mets-le à false pour masquer la tâche du panneau sans la supprimer. Par défaut : true

Si task_key existe déjà dans la base de données, la ligne est mise à jour (upsert). On peut l’appeler sans risque à chaque démarrage.


Enregistrer des améliorations

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
    },
})
Champ Type Obligatoire Description
upgrade_key string ✅ Identifiant unique
label string ✅ Nom affiché dans le panneau du gang
description string — Description plus longue
type string — Libellé de type libre pour le regroupement dans la NUI (p. ex. 'robbery_unlock', 'logistics', 'economy')
required_tasks table ou string — Tableau de valeurs task_key qui doivent être accomplies. Accepte aussi un string CSV : 'task_a, task_b'
effect table — Données arbitraires. effect.feature est la clé que les autres ressources interrogent via IsGangFeatureUnlocked
enabled boolean — Par défaut : true

Plusieurs tâches obligatoires

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

Toutes les tâches listées doivent être accomplies avant que l’amélioration puisse être débloquée. La NUI montre au gang l’état de chaque tâche.


Accomplir des tâches depuis ta ressource

Appelle-le chaque fois qu’un membre du gang termine l’activité correspondante en jeu :

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

Valeur renvoyée

Champ Type Description
ok boolean true en cas de succès
error string|nil 'already_completed' / 'task_not_found' / 'invalid_gang'
times_completed number Nombre de fois où elle a été accomplie après cet appel

Vérifier l’accomplissement d’une tâche

IsGangTaskCompleted(gangId, taskKey) → boolean

Vérification synchrone en mémoire. Utilise-la dans les callbacks serveur avant d’autoriser une 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)

Débloquer des améliorations

Les améliorations sont normalement débloquées par le chef du gang, ou par un membre disposant de la permission de grade manage_org_upgrades, depuis la NUI. Tu peux aussi les débloquer par code, ce qui est utile pour des récompenses automatiques ou des événements serveur :

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

Valeur renvoyée

Champ Type Description
ok boolean true en cas de succès
error string|nil 'already_unlocked' / 'missing_dependencies' / 'upgrade_not_found' / 'invalid_gang'
missing_tasks { task_key, label }[] Présent uniquement quand error == 'missing_dependencies'

Restreindre du contenu avec des feature keys

C’est le modèle recommandé. Ta ressource ne connaît pas la upgrade_key, seulement le string feature. Tu pourras renommer l’amélioration plus tard sans toucher aux autres ressources.

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)

Lire l’état complet

GetGangTasks(gangId) → table[]

Renvoie le catalogue complet enrichi de la progression de chaque gang :

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

Renvoie le catalogue complet des améliorations avec l’état de déblocage et la résolution des dépendances :

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

Le fichier de hooks se trouve dans custom/server/org_panels_hooks.lua. Il est chargé par la ressource et exécuté dans origen_ilegalv2, sans besoin de liaison entre ressources.

Modifie uniquement le corps de chaque fonction. Ne renomme ni les fonctions ni la table OrgPanelsHooks.

OrgPanelsHooks.onTaskCompleted(ctx)

Se déclenche chaque fois qu’une tâche est marquée comme accomplie pour un gang, y compris les tâches répétables.

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 déclenche exactement une fois par paire (gangId, upgradeKey), car les améliorations sont 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 déclenche quand quelqu’un tente de débloquer une amélioration alors que les tâches requises ne sont pas encore accomplies. Utile pour journaliser ou donner un retour supplémentaire.

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

Exemple d’intégration complet

Voici une version condensée de ce à quoi ressemble une intégration complète depuis une ressource externe :

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)