Inicio › Documentación › Laboratorios personalizados · origen_ilegalv2

Laboratorios personalizados · origen_ilegalv2

origen_ilegalv2 incluye un Lab Process Runner genérico que se encarga de todo el código repetitivo de un laboratorio de producción: puntos de interacción, markers, barras de progreso, animaciones, validación en el servidor, consumo de items y modificadores de mejoras. Para añadir un nuevo laboratorio de droga solo necesitas escribir una tabla de configuración y llamar a una función.


Cómo funciona

El runner tiene dos lados que se reflejan entre sí:

Lado Fichero Responsabilidad
Cliente client/lab_process_runner.lua lib.points, markers, HelpText, barras de progreso, animaciones, eventos del ciclo de vida
Servidor server/lab_process_runner.lua Validación de callbacks, comprobación de items, consumo/entrega de items, modificadores de mejoras, cooldowns

Ambos lados se registran con la misma clave de tipo de laboratorio y se comunican mediante callbacks de ox_lib con nombre automático:

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

Laboratorio mínimo viable

1. Crea el fichero de configuración

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

El orden de Stages es el orden de ejecución obligatorio. El runner lo impone: un jugador no puede saltar a la fase N+1 sin completar antes la fase N.

2. Regístralo en el 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. Regístralo en el cliente

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

4. Añade el tipo de laboratorio a Config.LabTypes

El runner no crea el laboratorio en la base de datos: debes registrar un tipo de laboratorio para que el panel de administración pueda crear instancias de él:

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. Añádelo a fxmanifest.lua

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

6. Añade las claves de traducción

Para cada fase necesitas tres claves de traducción (en 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',

Referencia completa de la configuración

Array Stages

Cada entrada define un paso de la producción:

Campo Tipo Obligatorio Descripción
id string ✅ Identificador único de la fase. Se usa en las claves de Coords, Timers, Markers, Animations y Recipe
label_key string ✅ Sufijo de traducción para la barra de progreso: notify.{labType}_{label_key}
help_key string — Sufijo de traducción para el HelpText: notify.{labType}_help_{help_key}. Por defecto, label_key
done_key string — Sufijo de traducción para la notificación de fase completada. Por defecto, {label_key}_done
timer number — Temporizador de la fase en ms (informativo: el runner usa Timers[id] en tiempo de ejecución)

Tabla Recipe

Indexada por stage_id. Cada entrada admite:

Campo Tipo Descripción
inputs { item, amount }[] Se consumen al empezar la fase
outputs { item, amount }[] Se entregan al completar la fase
requires { item, amount }[] Se validan pero NO se consumen (herramientas, licencias)

Solo las fases que consumen o producen items necesitan una entrada. Las fases sin entrada en Recipe son transiciones «libres».

Tabla Markers

Indexada por stage_id. Todos los campos son opcionales: el runner recurre a un marker por defecto:

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

Tabla Animations

Indexada por stage_id. Dos modos:

Simple (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,
},

En red (NetworkCreateSynchronisedScene), con 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 defecto, la primera fase de cualquier laboratorio basado en el runner requiere tener desbloqueada una mejora del módulo style en esa instancia del laboratorio. Para desactivar esta restricción (útil mientras pruebas o para laboratorios de acceso libre):

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

MaxDistPerStage

Distancia máxima (en metros), global o por fase, para la validación de la interacción en el 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 mejoras

Cuando un laboratorio tiene mejoras del módulo style desbloqueadas (desde origen_gang_lab_upgrade_unlocks), el runner aplica automáticamente:

Modificador Efecto Acumulación
process_time_mult Multiplica todos los temporizadores de las fases (mínimo 0.70, es decir, un 30 % máximo de reducción) Multiplicativa
output_bonus_pct % de probabilidad de dar +1 unidad en la salida de la última fase (máximo 25 %) El máximo de todas las variantes

Se calculan en el servidor al inicio de cada ciclo y se devuelven al cliente como timers y modifiers en la respuesta del callback de la primera fase.


Laboratorio de marihuana: flujo especial

El laboratorio de marihuana (Config.LabWeed) no usa el runner genérico. Tiene un flujo propio:

Paso Acción Items
0 — Plantas Recoge 14 plantas (reaparecen tras PlantRespawnTime s) → wetcannabis ×1 cada una
1 — Secado Coloca wetcannabis en uno de los 4 puntos de secado independientes wetcannabis → drycannabis tras DryWait ms
2 — Molido Usa el molinillo (requiere weedgrinder, que no se consume) drycannabis → grindedweed
3 — Envasado Usa la envasadora grindedweed + bluntwrap → blunt

Los pasos 1–3 son independientes: no hay una secuencia obligatoria entre ellos. Solo necesitas el item de entrada correspondiente.

El tiempo de los puntos de secado se controla en el servidor (marca de tiempo ready_at); el temporizador del cliente es solo visual.