DRS Crafting

Bancadas de fabrico com interface moderna, níveis por skill, probabilidade de sucesso, ferramentas que não se gastam e fabrico em lote.

v2.2.8 QBCoreQboxESX drs_crafting
Ver na loja

Bancadas de fabrico com níveis por skill, probabilidade de sucesso, ferramentas que não se gastam, fabrico em lote e uma interface moderna com pesquisa e categorias. Cada fabrico é validado no servidor do princípio ao fim.

#Requisitos

FrameworkQBCore, Qbox ou ESX (deteção automática)
Inventárioox_inventory, qb-inventory, ps-inventory, lj-inventory ou qs-inventory
Base de dadosoxmysql
Interaçãoox_target, qb-target ou tecla E

#Instalação

  1. Coloca a pasta drs_crafting em resources e adiciona ao server.cfg, depois do oxmysql, do framework e do inventário:
server.cfg
ensure drs_crafting
  1. A tabela drs_crafting_xp é criada sozinha.
  2. Define as bancadas (Config.Stations) e as receitas (Config.Recipes). As receitas de exemplo usam itens base do QBCore (metalscrap, plastic, steel…).

#Bancadas

config.lua
{
    id = 'workbench',
    label = 'Bancada de trabalho',
    skill = 'general',                           -- cada skill tem o seu nível
    coords = vector4(1175.4, 2640.1, 37.75, 0.0),
    prop = 'prop_tool_bench02',                  -- nil = usa um objeto que já existe no mapa
    blip = { sprite = 566, color = 47, scale = 0.7 },
    jobs = nil,                                  -- ex.: { mechanic = 0 } (emprego = cargo mínimo)
    gangs = nil,
    recipes = { 'lockpick', 'repairkit', 'radio' },
},

#Receitas

config.lua
advancedlockpick = {
    item = 'advancedlockpick', amount = 1, time = 12, chance = 85, level = 5, xp = 25, category = 'tools',
    ingredients = { { item = 'metalscrap', amount = 12 }, { item = 'steel', amount = 4 } },
    tools = { { item = 'screwdriverset' } },     -- precisas de a ter, não se gasta
},
CampoDescrição
item / amountO que se recebe e quantos por vez
timeSegundos por unidade (validado no servidor)
chanceProbabilidade de sucesso (%)
level / xpNível mínimo da skill e XP por unidade
ingredientsMateriais gastos
toolsFerramentas/planos que não se gastam
category, label, description, imageOpcionais, para a interface

Se o fabrico falhar, Config.OnFail decide: lose (perde os materiais), keep ou half. O jogador ganha Config.XPOnFail do XP mesmo a falhar.

#Níveis

Cada skill (ex.: general, weapons) sobe em separado. O XP de um nível para o seguinte é Config.Levels.base × growth^(nível-1).

#Comandos

ComandoQuemO que faz
/craftxp [id] [skill] [quantidade]staffDá ou tira XP numa skill

#Configuração completa

config.lua
Config = {}

-- Idioma / Language: 'pt' ou 'en'
Config.Locale = 'pt'

-- Framework: 'auto' deteta Qbox/QBCore/ESX (ou 'qbx' | 'qb' | 'esx')
Config.Framework = 'auto'

-- Como se abre a bancada / How a bench is opened
--   'auto' (ox_target > qb-target > tecla) | 'ox_target' | 'qb-target' | 'key'
Config.Interaction = 'auto'
Config.InteractKey = 38            -- E (só no modo 'key' / only in 'key' mode)
Config.InteractKeyLabel = 'E'
Config.InteractDistance = 2.0      -- também validado no servidor / also checked on the server

-- Imagens dos itens. 'auto' usa a pasta do inventário que estiver a correr.
-- Item images. 'auto' uses the folder of the running inventory.
-- Ou / Or: 'nui://o-meu-inventario/html/images/%s.png'
Config.ImagePath = 'auto'

-- ------------------------------------------------------------------ níveis / levels
Config.Levels = {
    enabled = true,
    max = 50,
    base = 100,        -- XP do nível 1 para o 2 / XP from level 1 to 2
    growth = 1.25,     -- cada nível pede mais 25% / each level needs 25% more
}

-- Se falhar: 'lose' perde os ingredientes, 'keep' fica com eles, 'half' perde metade
-- On failure: 'lose' loses the ingredients, 'keep' keeps them, 'half' loses half
Config.OnFail = 'lose'
Config.XPOnFail = 0.5              -- fração do XP que ganha mesmo quando falha / share of XP given on failure

Config.MaxQuantity = 10            -- máximo por vez / max per batch
Config.Animation = { dict = 'mini@repair', clip = 'fixing_a_ped' }

-- ------------------------------------------------------------------ bancadas / benches
-- skill: cada skill tem o seu nível (ex.: 'weapons' e 'general' sobem em separado)
-- skill: each skill has its own level (e.g. 'weapons' and 'general' level up separately)
-- jobs / gangs: quem pode usar (nil = todos). Exemplo: jobs = { mechanic = 0 } (emprego = cargo mínimo)
-- jobs / gangs: who can use it (nil = everyone). Example: jobs = { mechanic = 0 } (job = minimum grade)
-- prop: objeto criado no sítio (nil = usa o que já existe no mapa) / object spawned there (nil = use what the map has)
Config.Stations = {
    {
        id = 'workbench',
        label = 'Bancada de trabalho',
        skill = 'general',
        coords = vector4(1175.4, 2640.1, 37.75, 0.0),
        prop = 'prop_tool_bench02',
        blip = { sprite = 566, color = 47, scale = 0.7 },
        recipes = { 'lockpick', 'advancedlockpick', 'repairkit', 'advancedrepairkit', 'radio', 'screwdriverset', 'electronickit', 'drill' },
    },
    {
        id = 'armory',
        label = 'Bancada de armas',
        skill = 'weapons',
        coords = vector4(2195.64, 5594.35, 53.77, 254.0),
        prop = 'gr_prop_gr_bench_04a',
        blip = false,
        gangs = nil,
        recipes = { 'armor', 'weapon_pistol' },
    },
}

-- ------------------------------------------------------------------ receitas / recipes
-- item: o que se recebe / what you get · amount: quantos por vez / how many each time
-- time: segundos por unidade / seconds per unit · chance: % de sucesso / success %
-- level: nível mínimo da skill / minimum skill level · xp: XP por unidade / XP per unit
-- ingredients: gastos / consumed · tools: precisas de os ter, não são gastos (plantas, ferramentas) / needed, not consumed
-- category: separador na interface / tab in the UI · label/description: opcionais (senão usa o nome do item)
Config.Recipes = {
    lockpick = {
        item = 'lockpick', amount = 1, time = 6, chance = 100, level = 1, xp = 10, category = 'tools',
        ingredients = { { item = 'metalscrap', amount = 8 }, { item = 'plastic', amount = 4 } },
    },
    advancedlockpick = {
        item = 'advancedlockpick', amount = 1, time = 12, chance = 85, level = 5, xp = 25, category = 'tools',
        ingredients = { { item = 'metalscrap', amount = 12 }, { item = 'steel', amount = 4 }, { item = 'plastic', amount = 6 } },
        tools = { { item = 'screwdriverset' } },
    },
    screwdriverset = {
        item = 'screwdriverset', amount = 1, time = 10, chance = 100, level = 2, xp = 15, category = 'tools',
        ingredients = { { item = 'steel', amount = 6 }, { item = 'plastic', amount = 6 }, { item = 'rubber', amount = 2 } },
    },
    drill = {
        item = 'drill', amount = 1, time = 25, chance = 80, level = 12, xp = 60, category = 'tools',
        ingredients = { { item = 'steel', amount = 15 }, { item = 'copper', amount = 10 }, { item = 'electronickit', amount = 1 }, { item = 'rubber', amount = 6 } },
    },
    repairkit = {
        item = 'repairkit', amount = 1, time = 10, chance = 100, level = 1, xp = 12, category = 'vehicles',
        ingredients = { { item = 'metalscrap', amount = 10 }, { item = 'steel', amount = 4 }, { item = 'rubber', amount = 4 } },
    },
    advancedrepairkit = {
        item = 'advancedrepairkit', amount = 1, time = 18, chance = 90, level = 8, xp = 35, category = 'vehicles',
        ingredients = { { item = 'metalscrap', amount = 18 }, { item = 'steel', amount = 10 }, { item = 'aluminum', amount = 8 }, { item = 'rubber', amount = 8 } },
        tools = { { item = 'screwdriverset' } },
    },
    radio = {
        item = 'radio', amount = 1, time = 12, chance = 95, level = 3, xp = 20, category = 'electronics',
        ingredients = { { item = 'plastic', amount = 10 }, { item = 'copper', amount = 6 }, { item = 'glass', amount = 2 } },
    },
    electronickit = {
        item = 'electronickit', amount = 1, time = 15, chance = 90, level = 6, xp = 30, category = 'electronics',
        ingredients = { { item = 'copper', amount = 12 }, { item = 'plastic', amount = 8 }, { item = 'aluminum', amount = 4 } },
        tools = { { item = 'screwdriverset' } },
    },
    armor = {
        item = 'armor', amount = 1, time = 20, chance = 90, level = 4, xp = 30, category = 'protection',
        ingredients = { { item = 'steel', amount = 12 }, { item = 'rubber', amount = 8 }, { item = 'aluminum', amount = 6 } },
    },
    weapon_pistol = {
        item = 'weapon_pistol', amount = 1, time = 45, chance = 75, level = 10, xp = 80, category = 'weapons',
        ingredients = { { item = 'steel', amount = 25 }, { item = 'metalscrap', amount = 30 }, { item = 'rubber', amount = 6 }, { item = 'aluminum', amount = 10 } },
        tools = { { item = 'screwdriverset' } },
    },
}

-- Nomes das categorias / Category names
Config.Categories = {
    pt = { tools = 'Ferramentas', vehicles = 'Veículos', electronics = 'Eletrónica', protection = 'Proteção', weapons = 'Armas' },
    en = { tools = 'Tools', vehicles = 'Vehicles', electronics = 'Electronics', protection = 'Protection', weapons = 'Weapons' },
}

-- Comando de staff para dar/tirar XP: /craftxp [id] [skill] [quantidade]
-- Staff command to give/take XP: /craftxp [id] [skill] [amount]
Config.AdminCommand = 'craftxp'
Config.StaffGroups = { 'god', 'admin', 'superadmin' }

-- Webhook do Discord (vazio = desligado) / Discord webhook (empty = off)
Config.Webhook = ''

-- Notificações com ox_lib quando estiver a correr / Use ox_lib notifications when it is running
Config.UseOxNotify = false

#Para programadores

server
exports.drs_crafting:AddXP(src, 'weapons', 50)
exports.drs_crafting:GetLevel(src, 'weapons')
exports.drs_crafting:GetXP(src, 'general')

#Segurança

  • Ao começar, o servidor confirma a distância à bancada, o acesso, o nível, os materiais e as ferramentas.
  • Ao acabar, confirma o tempo, a distância e os materiais outra vez — e só então tira e entrega.
  • Se o inventário estiver cheio, os materiais das unidades que não couberam são devolvidos.

#Resolução de problemas

As imagens dos itens não aparecem — o script usa a pasta do inventário que estiver a correr. Se usas outro, define Config.ImagePath (ex.: nui://o-meu-inventario/html/images/%s.png).

"Não tens acesso a esta bancada." — vê jobs/gangs da bancada e da receita.

O objeto da bancada está no ar ou enterrado — ajusta as coords; o objeto é colocado no chão sozinho, mas precisa de um ponto livre.