Início › Documentação › Laboratórios personalizados · origen_ilegalv2

Laboratórios personalizados · origen_ilegalv2

O origen_ilegalv2 inclui um Lab Process Runner genérico que trata de todo o código repetitivo de um laboratório de produção: pontos de interação, markers, barras de progresso, animações, validação no servidor, consumo de items e modificadores de melhorias. Para adicionar um novo laboratório de droga só precisas de escrever uma tabela de configuração e chamar uma função.


Como funciona

O runner tem dois lados que se espelham:

Lado Ficheiro Responsabilidade
Cliente client/lab_process_runner.lua lib.points, markers, HelpText, barras de progresso, animações, eventos do ciclo de vida
Servidor server/lab_process_runner.lua Validação de callbacks, verificação de items, consumo/entrega de items, modificadores de melhorias, cooldowns

Ambos os lados registam-se com a mesma chave de tipo de laboratório e comunicam através de callbacks do ox_lib com nome automático:

text
origen_gang:labs:{labType}:stage_{stageId}

Laboratório mínimo viável

1. Cria o ficheiro de configuração

luaconfig/lab_heroin.lua
Config.LabHeroin = {
    Enabled = true,
 
    -- Ordered production stages
    Stages = {
        { id = 'mix',      label_key = 'mixing',   help_key = 'mix',  done_key = 'mix_done',    timer = 10000 },
        { id = 'filter',   label_key = 'filtering', help_key = 'filter', done_key = 'filter_done', timer = 8000  },
        { id = 'bag',      label_key = 'bagging',  help_key = 'bag',  done_key = 'bag_done',    timer = 6000  },
    },
 
    ProductionCooldown = 600000,  -- 10 minutes between full cycles (ms)
 
    Interactions = {
        PointDistance    = 40.0,   -- lib.points render radius
        DrawDistance     = 8.0,    -- marker draw distance
        InteractDistance = 1.8,    -- HelpText / [E] distance
    },
 
    -- World coords for each stage (interior coords inside the lab bucket)
    Coords = {
        mix    = vector3(1006.09, -3200.59, -38.52),
        filter = vector3(1007.89, -3201.17, -38.99),
        bag    = vector3(1014.25, -3194.93, -38.99),
    },
 
    -- Progress bar durations per stage (ms)
    Timers = {
        mix    = 10000,
        filter = 8000,
        bag    = 6000,
    },
 
    -- Items consumed/delivered per stage
    -- inputs:   consumed when the stage starts
    -- outputs:  delivered when the stage completes
    -- requires: validated but NOT consumed (tools)
    Recipe = {
        mix = {
            inputs = {
                { item = 'opium_raw',  amount = 2 },
                { item = 'acetic_acid', amount = 1 },
            },
        },
        bag = {
            inputs  = { { item = 'empty_bag', amount = 3 } },
            outputs = { { item = 'heroin_bag', amount = 3 } },
        },
    },
}

A ordem de Stages é a ordem de execução obrigatória. O runner impõe-na: um jogador não pode saltar para a fase N+1 sem concluir primeiro a fase N.

2. Regista-o no servidor

luaserver/lab_heroin.lua
if not Config.LabHeroin or not Config.LabHeroin.Enabled then return end
 
LabProcessRunner.Register('heroin', Config.LabHeroin)
 
-- Optional: expose a reset export for external resources
exports('HeroinLabResetPlayerState', function(source)
    LabProcessRunner.ResetPlayerState('heroin', source)
end)

3. Regista-o no cliente

luaclient/lab_heroin.lua
if not Config.LabHeroin or not Config.LabHeroin.Enabled then return end
 
LabProcessRunner.Register('heroin', Config.LabHeroin)

4. Adiciona o tipo de laboratório a Config.LabTypes

O runner não cria o laboratório na base de dados: tens de registar um tipo de laboratório para que o painel de administração possa criar instâncias dele:

luaconfig/labs.lua
Config.LabTypes = {
    -- ...existing types...
    heroin = {
        label = 'Heroin Lab',
        interior = {
            spawn  = vector4(997.17, -3200.64, -37.39, 270.0),
            laptop = vector4(1002.01, -3194.89, -38.99, 356.98),
        }
    },
}

5. Adiciona-o ao fxmanifest.lua

luafxmanifest.lua
shared_scripts {
    -- ...
    'config/lab_heroin.lua',
}
 
server_scripts {
    -- ...
    'server/lab_heroin.lua',
}
 
client_scripts {
    -- ...
    'client/lab_heroin.lua',
}

6. Adiciona as chaves de tradução

Para cada fase precisas de três chaves de tradução (em locales/translations/en.lua):

lua
-- Progress bar label
['notify.heroin_mixing']      = 'Mixing...',
['notify.heroin_filtering']   = 'Filtering...',
['notify.heroin_bagging']     = 'Packaging...',
 
-- HelpText (shown when in range)
['notify.heroin_help_mix']    = 'Press [E] to mix chemicals',
['notify.heroin_help_filter'] = 'Press [E] to filter the mixture',
['notify.heroin_help_bag']    = 'Press [E] to bag the product',
 
-- Stage-done notifications
['notify.heroin_mix_done']    = 'Mixture ready',
['notify.heroin_filter_done'] = 'Filtration complete',
['notify.heroin_bag_done']    = 'Product packaged',  -- last stage receives output amount as arg
 
-- Error messages used by the runner
['notify.heroin_not_in_lab']             = 'You are not inside the lab',
['notify.heroin_wrong_lab_type']         = 'Wrong lab type',
['notify.heroin_on_cooldown']            = 'Wait before starting another cycle',
['notify.heroin_already_in_progress']    = 'Production already in progress',
['notify.heroin_wrong_stage']            = 'Complete the previous step first',
['notify.heroin_requires_basic_upgrade'] = 'Upgrade the lab before processing',

Referência completa da configuração

Array Stages

Cada entrada define um passo da produção:

Campo Tipo Obrigatório Descrição
id string ✅ Identificador único da fase. Usa-se nas chaves de Coords, Timers, Markers, Animations e Recipe
label_key string ✅ Sufixo de tradução da barra de progresso: notify.{labType}_{label_key}
help_key string — Sufixo de tradução do HelpText: notify.{labType}_help_{help_key}. Por omissão, label_key
done_key string — Sufixo de tradução da notificação de fase concluída. Por omissão, {label_key}_done
timer number — Temporizador da fase em ms (informativo: o runner usa Timers[id] em tempo de execução)

Tabela Recipe

Indexada por stage_id. Cada entrada suporta:

Campo Tipo Descrição
inputs { item, amount }[] Consumidos quando a fase começa
outputs { item, amount }[] Entregues quando a fase termina
requires { item, amount }[] Validados mas NÃO consumidos (ferramentas, licenças)

Só as fases que consomem ou produzem items precisam de uma entrada. As fases sem entrada em Recipe são transições «livres».

Tabela Markers

Indexada por stage_id. Todos os campos são opcionais: o runner recorre a um marker por omissão:

lua
Markers = {
    mix = {
        type          = 22,
        offset        = { x = 0.0, y = 0.0, z = 0.0 },
        scale         = { x = 0.25, y = 0.25, z = 0.25 },
        color         = { r = 0, g = 0, b = 0, a = 180 },
        bobUpAndDown  = true,
        faceCamera    = true,
        rotationOrder = 2,
        rotate        = false,
    },
},

Tabela Animations

Indexada por stage_id. Dois modos:

Simples (TaskPlayAnim):

lua
mix = {
    networked = false,
    dict      = 'anim@amb@business@meth@meth_monitoring_cooking@',
    clip      = 'idle_a',
    flag      = 49,
    blendIn   = 4.0,
    blendOut  = -8.0,
    freeze    = false,
},

Em rede (NetworkCreateSynchronisedScene), com props:

lua
cook = {
    networked = true,
    dict      = 'anim@amb@business@meth@meth_monitoring_cooking@cooking@',
    clip      = 'chemical_pour_short_cooker',
    flag      = 49,
    blendIn   = 1.5,
    blendOut  = -4.0,
    freeze    = true,
    offset    = vector3(4.79, 2.13, -0.41),
    rotation  = vector3(0.0, 0.0, 0.0),
    objects   = {
        { hash = 'bkr_prop_meth_sacid',   clip = 'chemical_pour_short_sacid'   },
        { hash = 'bkr_prop_meth_ammonia', clip = 'chemical_pour_short_ammonia' },
    },
},

Flag RequiresUpgrade

Por omissão, a primeira fase de qualquer laboratório baseado no runner exige que esteja desbloqueada uma melhoria do módulo style nessa instância do laboratório. Para desativar esta restrição (útil durante os testes ou para laboratórios de acesso livre):

lua
Config.LabHeroin = {
    RequiresUpgrade = false,  -- skip the upgrade gate
    -- ...
}

MaxDistPerStage

Distância máxima (em metros), global ou por fase, para a validação da interação no servidor:

lua
-- Global: same distance for every stage
MaxDistPerStage = 4.0,
 
-- Per-stage override
MaxDistPerStage = {
    mix    = 3.0,
    filter = 5.0,
    bag    = 3.0,
},

Modificadores de melhorias

Quando um laboratório tem melhorias do módulo style desbloqueadas (a partir de origen_gang_lab_upgrade_unlocks), o runner aplica automaticamente:

Modificador Efeito Acumulação
process_time_mult Multiplica todos os temporizadores das fases (mínimo 0.70, ou seja, uma redução máxima de 30 %) Multiplicativa
output_bonus_pct % de probabilidade de dar +1 unidade na saída da última fase (máximo 25 %) O máximo de todas as variantes

Calculam-se no servidor no início de cada ciclo e são devolvidos ao cliente como timers e modifiers na resposta do callback da primeira fase.


Laboratório de erva: fluxo especial

O laboratório de erva (Config.LabWeed) não usa o runner genérico. Tem um fluxo próprio:

Passo Ação Items
0 — Plantas Apanha 14 plantas (reaparecem após PlantRespawnTime s) → wetcannabis ×1 cada
1 — Secagem Coloca wetcannabis num dos 4 pontos de secagem independentes wetcannabis → drycannabis após DryWait ms
2 — Moagem Usa o moinho (requer weedgrinder, que não é consumido) drycannabis → grindedweed
3 — Embalagem Usa a máquina de embalar grindedweed + bluntwrap → blunt

Os passos 1–3 são independentes: não há uma sequência obrigatória entre eles. Só precisas do item de entrada correspondente.

O tempo dos pontos de secagem é controlado no servidor (marca temporal ready_at); o temporizador do cliente é apenas visual.