DRS Police Job

v2.5.2 drs_policejob
Ver na loja

Emprego de polícia completo: menu de ações no F6 e pontos da esquadra (serviço, armeiro, garagem, heliporto, vestiário, cacifos de provas, laboratório e gestão). Todas as ações são confirmadas no servidor: emprego, serviço, cargo, distância e estado da pessoa. Funciona com Qbox, QBCore e ESX.

Dica

O Police Job foi feito para trabalhar com os outros scripts DRS da polícia: o MDT recebe o BOLO, as provas do laboratório e as horas de serviço, a Prison recebe as penas e o Community Service o serviço comunitário. Nenhum é obrigatório.

#Requisitos

RecursoObrigatório
oxmysqlSim
Qbox, QBCore ou ESXSim (deteção automática)
qb-inventory, ox_inventory ou outroSim (revista, cacifos e armeiro)
ox_libNão — só se usares as notificações do ox (Config.UseOxNotify)
drs_prison, qb-prison, esx-qalle-jail ou pickle_prisonsNão — para a prisão
drs_communityserviceNão — para o serviço comunitário
drs_bossmenu, qb-management ou esx_societyNão — para o menu de gestão

#Instalação

  1. Coloca drs_policejob em resources.
  2. Adiciona ao server.cfg, depois do oxmysql e da framework:
server.cfg
ensure oxmysql
ensure qb-core
ensure drs_policejob
  1. Desliga o qb-policejob / esx_policejob. O drs_policejob responde por exports['qb-policejob'] (provide 'qb-policejob') e, com Config.QBCompat = true, mantém os eventos que outros scripts usam: contagem de polícias, police:server:policeAlert, ishandcuffed e os botões do qb-radialmenu.
  2. As tabelas são criadas sozinhas no primeiro arranque. Se preferires importar à mão:
drs_policejob.sql
-- DRS Police Job — as tabelas também são criadas sozinhas no arranque / tables are also created on start
CREATE TABLE IF NOT EXISTS drs_pd_duty (
    id INT AUTO_INCREMENT PRIMARY KEY, identifier VARCHAR(64) NOT NULL, name VARCHAR(100), job VARCHAR(50),
    started DATETIME NOT NULL, ended DATETIME NULL, seconds INT NULL, INDEX (identifier), INDEX (job, started));
CREATE TABLE IF NOT EXISTS drs_pd_evidence (
    id INT AUTO_INCREMENT PRIMARY KEY, type VARCHAR(20) NOT NULL, data LONGTEXT, street VARCHAR(120),
    collected_by VARCHAR(100), job VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    ready_at DATETIME NULL, result LONGTEXT NULL, INDEX (job));
CREATE TABLE IF NOT EXISTS drs_pd_fines (
    id INT AUTO_INCREMENT PRIMARY KEY, identifier VARCHAR(64), name VARCHAR(100), amount INT, reason VARCHAR(160),
    officer VARCHAR(100), job VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX (identifier));
CREATE TABLE IF NOT EXISTS drs_pd_bolos (
    plate VARCHAR(12) PRIMARY KEY, reason VARCHAR(160), officer VARCHAR(100), created_at DATETIME DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS drs_pd_officers (
    identifier VARCHAR(64) PRIMARY KEY, callsign VARCHAR(16));
  1. Adiciona os itens (ver Itens) e copia as imagens de install/images/ para o teu inventário.
  2. Ajusta os departamentos, os pontos da esquadra, as viaturas, o armeiro e as fardas no config.lua.

#Itens

O script usa três itens: handcuffs, empty_evidence_bag e filled_evidence_bag. No QBCore já existem; nos outros inventários, adiciona-os.

QBCore — dentro de qb-core/shared/items.lua:

items_qb-core.lua
-- drs_policejob: cola dentro de qb-core/shared/items.lua (Qbox: ox_inventory/data/items.lua, ver o outro ficheiro)
-- Imagens: copia install/images/*.png para qb-inventory/html/images/

    handcuffs = { name = 'handcuffs', label = 'Algemas', weight = 250, type = 'item', image = 'handcuffs.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Algemas da polícia' },
    empty_evidence_bag = { name = 'empty_evidence_bag', label = 'Saco de provas vazio', weight = 50, type = 'item', image = 'empty_evidence_bag.png', unique = false, useable = false, shouldClose = true, combinable = nil, description = 'Para recolher provas' },
    filled_evidence_bag = { name = 'filled_evidence_bag', label = 'Saco de provas', weight = 100, type = 'item', image = 'filled_evidence_bag.png', unique = false, useable = false, shouldClose = true, combinable = nil, description = 'Prova recolhida' },

ox_inventory / Qbox — dentro de ox_inventory/data/items.lua:

items_ox_inventory.lua
-- drs_policejob: cola dentro de ox_inventory/data/items.lua
-- Imagens: copia install/images/*.png para ox_inventory/web/images/

    ['handcuffs'] = { label = 'Algemas', weight = 250, stack = true, close = true, description = 'Algemas da polícia', client = { image = 'handcuffs.png' } },
    ['empty_evidence_bag'] = { label = 'Saco de provas vazio', weight = 50, stack = true, close = true, description = 'Para recolher provas', client = { image = 'empty_evidence_bag.png' } },
    ['filled_evidence_bag'] = { label = 'Saco de provas', weight = 100, stack = true, close = true, description = 'Prova recolhida', client = { image = 'filled_evidence_bag.png' } },

ESX — importa na base de dados:

items_esx.sql
-- drs_policejob: executa na base de dados (ESX com a tabela items)
INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES ('handcuffs', 'Algemas', 1, 0, 1);
INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES ('empty_evidence_bag', 'Saco de provas vazio', 1, 0, 1);
INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES ('filled_evidence_bag', 'Saco de provas', 1, 0, 1);

As imagens estão em install/images/: copia-as para qb-inventory/html/images/ ou ox_inventory/web/images/. Podes usar outros nomes de itens: muda-os em Config.Cuffs.Item e Config.Evidence.

#Teclas e comandos

Tecla / comandoO que faz
F6Abre o menu de ações (tens de estar de serviço)
NUMPAD8Liga/desliga o radar
NUMPAD5Tranca a leitura do radar
GRecolhe a prova mais perto
/callsign [indicativo]Define o teu indicativo
/pdadmin uncuff [id]Staff: tira as algemas a um jogador
/pdadmin duty [id]Staff: muda o serviço de um jogador

As teclas podem ser mudadas por cada jogador em Definições → Atalhos → FiveM. O /pdadmin precisa da ACE drs.policejob ou de um grupo de Config.StaffGroups:

server.cfg
add_ace group.admin drs.policejob allow

#Departamentos e cargos

Cada emprego em Config.Departments conta como polícia. Em grades defines o cargo mínimo para cada ação; o que não estiver na lista fica a 0 (todos).

config.lua
Config.Departments = {
    police  = { label = 'PSP', color = '#3b82f6', grades = { spikes = 1, jail = 1, bolo = 1, lab = 1, boss = 4, heli = 3 } },
    sheriff = { label = 'GNR', color = '#16a34a', grades = { boss = 4 } },
}
ChaveAção
finePassar multas
jailMandar para a prisão
communityServiço comunitário
impoundApreender viaturas
remove_tintRemover a película dos vidros
boloPôr e tirar matrículas da lista BOLO
spikesColocar a fita de pregos
labUsar o laboratório
evidence_stashAbrir os cacifos de provas
heliHeliporto
bossMenu de gestão

Com Config.RequireDuty = true, o menu e os pontos só funcionam de serviço.

#Funcionalidades

#Ações sobre o cidadão

  • Algemar normal (deixa andar, SoftCanWalk) ou apertado, e tirar as algemas. Precisa do item handcuffs (não se gasta com KeepItem = true). Com RequireSurrender = true, só algemas quem está de mãos no ar, de joelhos, morto ou de costas.
  • Escoltar, pôr no veículo e tirar do veículo.
  • Revistar: abre o inventário da pessoa (só algemada, de mãos no ar ou morta).
  • Identificação, impressões digitais, teste de pólvora (GSR, positivo durante Config.GSR.Minutes; a água limpa-o) e teste de álcool (as bebidas somam g/L; o limite legal é Config.Alcohol.Limit).

#Justiça

  • Multas com infrações predefinidas (Config.Fines.Presets) ou valor livre até Max. O dinheiro sai da conta escolhida e vai para o departamento (ToSociety) e/ou para o agente (OfficerShare).
  • Prisão até Config.Jail.MaxMonths, pelo drs_prison, qb-prison, esx-qalle-jail ou pickle_prisons.
  • Serviço comunitário pelo drs_communityservice.

#Viaturas e radar

  • Verificar a matrícula: dono, lista BOLO e película.
  • Remover a película, abrir a fechadura, apreender (fica apreendida na garagem) e guardar a viatura de serviço.
  • Radar de velocidade à frente e atrás, com matrícula e patrulha. NUMPAD5 tranca a leitura e o radar avisa quando passa uma matrícula da lista BOLO. Funciona nas classes/modelos de Config.Radar.

#Objetos

Cones, barreiras, grades, holofotes, tendas, púlpito e fita de pregos, com pré-visualização e rotação. São criados pelo servidor (um cliente não consegue criar objetos) e a fita rebenta os pneus em qualquer ângulo. Limite por agente em Config.Props.MaxPerOfficer.

#Provas e laboratório

  1. Quando alguém dispara, fica uma cápsula no chão (o servidor confirma a arma que a pessoa tem na mão). Quando alguém se fere, fica sangue.
  2. Só a polícia de serviço vê as provas: com a lanterna apontada ou no modo "procurar provas" do menu.
  3. Com G recolhes a prova mais perto: gasta um empty_evidence_bag e recebes um filled_evidence_bag com o número do registo.
  4. No laboratório, a análise demora AnalysisSeconds. O sangue dá o ADN e a correspondência com a pessoa; a cápsula dá a arma e o número de série (e quem disparou, se CasingRevealsShooter = true).

As provas no chão desaparecem ao fim de DecayMinutes.

#Esquadra

  • Serviço: entrar e sair, com contagem de horas (as tuas nos últimos 7 dias e as da equipa para a chefia).
  • Armeiro com carrinho, cargo mínimo e quantidade máxima por item. Pagamento em Config.Armory.Pay: free, society (conta do departamento) ou player (banco do agente).
  • Garagem e heliporto com viaturas por cargo (Config.Vehicles), matrícula com prefixo (Config.PlatePrefix), combustível e chaves.
  • Vestiário com fardas por cargo e por sexo (Config.Outfits); a roupa civil volta ao sair.
  • Cacifos de provas: 10 inventários partilhados (Config.EvidenceLockers).
  • Gestão: abre o drs_bossmenu, o qb-management ou o esx_society.

#Outros

  • Distintivo com foto, nome, cargo e indicativo, mostrado a quem está perto.
  • Alertas no mapa para a polícia de serviço (Config.AlertSeconds).
  • Registos no Discord (multas, prisão, armeiro, apreensões) em Config.Webhook.

#Opções principais

OpçãoPor defeitoDescrição
Config.Locale'pt'Idioma: pt ou en
Config.Framework'auto'auto, qbx, qb ou esx
Config.RequireDutytrueMenu e pontos só de serviço
Config.KeysF6 / NUMPAD5 / NUMPAD8Teclas por defeito
Config.Cuffs.RequireSurrenderfalseSó algema quem se rendeu
Config.Fines.Max50000Valor máximo de uma multa
Config.Fines.ToSociety1.0Parte da multa para o departamento
Config.Evidence.EnabledtrueSistema de provas
Config.Evidence.DecayMinutes30Tempo até as provas desaparecerem
Config.Radar.SpeedUnit'kmh'kmh ou mph
Config.Armory.Pay'free'free, society ou player
Config.QBCompattrueMantém os eventos do qb-policejob
Config.Webhook''Webhook do Discord para registos

#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'

-- Notificações do ox_lib em vez das da framework / ox_lib notifications instead of the framework ones
Config.UseOxNotify = false

-- Grupos de staff (também passam nas verificações de polícia com /pdadmin) / Staff groups
Config.StaffGroups = { 'god', 'admin', 'superadmin' }

Config.Debug = false

Config.Currency = '€'
-- Imagens dos itens no armeiro / Item images in the armory ('nui://ox_inventory/web/images/' no ox)
Config.ItemImages = 'nui://qb-inventory/html/images/'
-- Comando para o agente definir o indicativo / Command for the officer callsign
Config.CallsignCommand = 'callsign'
-- Segundos que o alerta fica no mapa e que o distintivo fica no ecrã / Alert blip and badge seconds
Config.AlertSeconds = 90
Config.BadgeSeconds = 6
-- Tablet na mão no laboratório / Tablet prop in the lab
Config.TabletAnim = true

-- ------------------------------------------------------------------ departamentos / departments
-- Empregos que contam como polícia / Jobs that count as police
-- grades = cargo mínimo para cada ação (os que não estão aqui ficam a 0)
-- grades = minimum grade for each action (anything not listed here is 0)
Config.Departments = {
    police = {
        label = 'PSP',
        color = '#3b82f6',
        grades = {
            spikes = 1, fine = 0, jail = 1, community = 0, impound = 0, remove_tint = 1,
            bolo = 1, lab = 1, evidence_stash = 0, boss = 4, heli = 3,
        },
    },
    -- sheriff = { label = 'GNR', color = '#16a34a', grades = { boss = 4 } },
}

-- É preciso estar de serviço para usar o menu e os pontos? / Must be on duty for the menu and points?
Config.RequireDuty = true

-- Teclas (o jogador pode mudar nas definições do GTA) / Keys (players can rebind them in GTA settings)
Config.Keys = {
    Menu = 'F6',          -- menu de ações / action menu
    RadarLock = 'NUMPAD5',-- trancar a leitura do radar / lock the radar reading
    RadarToggle = 'NUMPAD8',
}

-- Distância máxima para interagir com um jogador / Max distance to act on a player
Config.InteractDistance = 3.0

-- ------------------------------------------------------------------ algemas / handcuffs
Config.Cuffs = {
    Item = 'handcuffs',          -- item preciso para algemar (false = não precisa) / item needed (false = none)
    KeepItem = true,             -- o item não se gasta / the item is not used up
    UncuffItem = false,          -- ex.: 'cuffkeys' (false = só a polícia tira) / e.g. 'cuffkeys' (false = police only)
    -- Só se pode algemar quem está de mãos no ar, de joelhos, morto ou de costas para o agente
    -- Only players with hands up, kneeling, dead or facing away can be cuffed
    RequireSurrender = false,
    SoftCanWalk = true,          -- algemas "leves" deixam andar / soft cuffs let the player walk
}

-- Revista: abre o inventário do outro jogador / Search: opens the other player's inventory
-- Só se estiver algemado, de mãos no ar ou morto / Only when cuffed, hands up or dead
Config.Search = { Time = 5000 }

-- ------------------------------------------------------------------ multas / fines
Config.Fines = {
    Max = 50000,
    Account = 'bank',            -- de onde sai o dinheiro / where the money comes from
    AllowDebt = false,           -- true = tira mesmo sem saldo (fica negativo) / charge even without balance
    ToSociety = 1.0,             -- parte que vai para a conta do departamento (0-1) / share that goes to the society
    OfficerShare = 0.0,          -- parte que vai para o agente (0-1) / share that goes to the officer
    Presets = {
        { label = 'Excesso de velocidade', amount = 500 },
        { label = 'Condução perigosa', amount = 1500 },
        { label = 'Desobediência', amount = 2500 },
        { label = 'Porte de arma ilegal', amount = 7500 },
    },
}

-- Prisão (máximo de meses) / Jail (max months)
Config.Jail = { MaxMonths = 120 }

-- ------------------------------------------------------------------ provas / evidence
Config.Evidence = {
    Enabled = true,
    Casings = true,              -- cápsulas quando alguém dispara / casings when someone shoots
    Blood = true,                -- sangue quando alguém é ferido / blood when someone is hurt
    DecayMinutes = 30,           -- as provas no chão desaparecem ao fim de X minutos / ground evidence expires
    ViewDistance = 20.0,         -- distância a que o agente vê as provas com a lanterna / flashlight view distance
    CollectDistance = 1.5,
    BagItem = 'empty_evidence_bag',   -- item gasto a recolher (false = nenhum) / item used to collect (false = none)
    FilledItem = 'filled_evidence_bag',-- item recebido (false = só fica na base de dados) / item given (false = DB only)
    AnalysisSeconds = 90,        -- tempo que o laboratório demora / lab analysis time
    CasingRevealsShooter = false,-- a cápsula mostra quem disparou? (senão só a arma e o número de série)
    IgnorePolice = false,        -- os polícias de serviço não deixam cápsulas / on-duty police leave no casings
    -- Armas que não deixam cápsulas / Weapons that leave no casings
    IgnoreWeapons = { 'WEAPON_STUNGUN', 'WEAPON_FLARE', 'WEAPON_PETROLCAN', 'WEAPON_FIREEXTINGUISHER', 'WEAPON_SNOWBALL', 'WEAPON_BALL' },
}

-- Teste de pólvora: minutos em que um atirador dá positivo (a água limpa) / GSR: minutes a shooter tests positive (water clears it)
Config.GSR = { Minutes = 20 }

-- Álcool: g/L somados por bebida (pode ser chamado por outros scripts: exports.drs_policejob:AddAlcohol(src, valor))
-- Alcohol: g/L added per drink (other scripts can call exports.drs_policejob:AddAlcohol(src, value))
Config.Alcohol = {
    Limit = 0.5,                 -- acima disto é crime / above this is illegal
    DecayPerMinute = 0.02,
    Items = { beer = 0.25, whiskey = 0.45, vodka = 0.5, wine = 0.3 },
}

-- ------------------------------------------------------------------ objetos / props
Config.Props = {
    MaxPerOfficer = 15,
    SpikesSingleUse = false,     -- a fita desaparece depois de rebentar pneus / spikes vanish after a hit
    PlaceDistance = 6.0,
    List = {
        { id = 'cone',    label = 'Cone',                model = 'prop_roadcone02a',     freeze = false },
        { id = 'barrier', label = 'Barreira',            model = 'prop_barrier_work06a', freeze = true },
        { id = 'fence',   label = 'Grade metálica',      model = 'prop_barrier_02a',     freeze = true },
        { id = 'light',   label = 'Holofote',            model = 'prop_worklight_03b',   freeze = true },
        { id = 'tent',    label = 'Tenda',               model = 'prop_gazebo_03',       freeze = true },
        { id = 'stand',   label = 'Púlpito',             model = 'p_amb_cop_stand',      freeze = true },
        { id = 'spikes',  label = 'Fita de pregos',      model = 'p_ld_stinger_s',       freeze = true, spikes = true, grade = 'spikes' },
    },
}

-- ------------------------------------------------------------------ radar
Config.Radar = {
    SpeedUnit = 'kmh',           -- 'kmh' | 'mph'
    Range = 60.0,
    FastLimit = 120,             -- acima disto o número fica vermelho / above this the reading turns red
    -- Classes de veículo onde o radar funciona (18 = emergência) / Vehicle classes with radar
    Classes = { [18] = true },
    Models = {},                 -- ou modelos específicos / or specific models: { 'police', 'police2' }
}

-- ------------------------------------------------------------------ pontos da esquadra / station points
-- type: duty | armory | garage | heli | wardrobe | evidence | lab | boss | impound
Config.Points = {
    { type = 'duty',     coords = vector3(440.08, -974.92, 30.69) },
    { type = 'duty',     coords = vector3(-449.81, 6012.91, 31.82) },
    { type = 'armory',   coords = vector3(462.23, -981.12, 30.68) },
    { type = 'wardrobe', coords = vector3(461.45, -997.66, 30.69) },
    { type = 'evidence', coords = vector3(442.17, -996.07, 30.69) },
    { type = 'lab',      coords = vector3(458.40, -989.20, 24.92) },
    { type = 'boss',     coords = vector3(447.79, -973.71, 30.69) },
    { type = 'garage',   coords = vector3(454.60, -1017.40, 28.43), spawns = {
        vector4(448.16, -1017.41, 28.56, 90.65),
        vector4(446.25, -1025.10, 28.64, 1.50),
        vector4(438.42, -1026.24, 28.79, 2.50),
    } },
    { type = 'garage',   coords = vector3(-457.50, 6006.00, 31.34), spawns = { vector4(-455.39, 6002.02, 31.34, 87.93) } },
    { type = 'heli',     coords = vector3(461.10, -981.60, 43.69), spawns = { vector4(449.17, -981.33, 43.69, 87.23) } },
}

-- Marcador no chão / Ground marker
Config.Marker = { type = 2, size = 0.3, color = { 59, 130, 246, 180 }, drawDistance = 12.0, useDistance = 1.6 }

-- Blips das esquadras / Station blips
Config.Blips = {
    { label = 'Esquadra da PSP', coords = vector3(428.23, -984.28, 29.76), sprite = 60, color = 29, scale = 0.85 },
    { label = 'Esquadra de Paleto', coords = vector3(-451.55, 6014.25, 31.72), sprite = 60, color = 29, scale = 0.75 },
}

-- ------------------------------------------------------------------ garagem / garage
Config.PlatePrefix = 'PSP'       -- matrículas: PSP + 5 números / plates: prefix + digits
Config.FuelResource = 'LegacyFuel'
-- Evento das chaves (cliente, recebe a matrícula) / Vehicle keys event (client, receives the plate)
Config.VehicleKeysEvent = 'vehiclekeys:client:SetOwner'

Config.Vehicles = {
    car = {
        { model = 'police',   label = 'Stanier',          grade = 0 },
        { model = 'police2',  label = 'Buffalo',          grade = 1 },
        { model = 'police3',  label = 'Interceptor',      grade = 2 },
        { model = 'policeb',  label = 'Mota',             grade = 1 },
        { model = 'policet',  label = 'Carrinha',         grade = 2 },
        { model = 'fbi2',     label = 'Granger (Descaracterizado)', grade = 3 },
        { model = 'riot',     label = 'Blindado',         grade = 4 },
    },
    heli = {
        { model = 'polmav',   label = 'Maverick',         grade = 3 },
    },
}

-- ------------------------------------------------------------------ armeiro / armory
-- pay = 'free' | 'society' (sai da conta do departamento) | 'player' (sai do banco do agente)
Config.Armory = {
    Pay = 'free',
    Categories = { weapons = 'Armas', ammo = 'Munições', gear = 'Equipamento' },
    Items = {
        { item = 'weapon_stungun',       label = 'Taser',              category = 'weapons', grade = 0, price = 0,   max = 1 },
        { item = 'weapon_nightstick',    label = 'Bastão',             category = 'weapons', grade = 0, price = 0,   max = 1 },
        { item = 'weapon_flashlight',    label = 'Lanterna',           category = 'weapons', grade = 0, price = 0,   max = 1 },
        { item = 'weapon_combatpistol',  label = 'Pistola de serviço', category = 'weapons', grade = 0, price = 0,   max = 1 },
        { item = 'weapon_pumpshotgun',   label = 'Caçadeira',          category = 'weapons', grade = 2, price = 0,   max = 1 },
        { item = 'weapon_carbinerifle',  label = 'Carabina',           category = 'weapons', grade = 3, price = 0,   max = 1 },
        { item = 'pistol_ammo',          label = 'Munição de pistola', category = 'ammo',    grade = 0, price = 0,   max = 10 },
        { item = 'shotgun_ammo',         label = 'Munição de caçadeira', category = 'ammo',  grade = 2, price = 0,   max = 10 },
        { item = 'rifle_ammo',           label = 'Munição de carabina', category = 'ammo',   grade = 3, price = 0,   max = 10 },
        { item = 'handcuffs',            label = 'Algemas',            category = 'gear',    grade = 0, price = 0,   max = 2 },
        { item = 'empty_evidence_bag',   label = 'Saco de provas',     category = 'gear',    grade = 0, price = 0,   max = 20 },
        { item = 'armor',                label = 'Colete',             category = 'gear',    grade = 0, price = 0,   max = 2 },
        { item = 'radio',                label = 'Rádio',              category = 'gear',    grade = 0, price = 0,   max = 1 },
        { item = 'gps',                  label = 'GPS',                category = 'gear',    grade = 0, price = 0,   max = 1 },
        { item = 'bodycam',              label = 'Bodycam',            category = 'gear',    grade = 0, price = 0,   max = 1 },
        { item = 'dog_food',             label = 'Ração para cão (K9)', category = 'gear',   grade = 0, price = 0,   max = 5 },
    },
}

-- Cacifos de provas (um inventário por cacifo) / Evidence lockers (one inventory each)
Config.EvidenceLockers = { count = 10, slots = 50, weight = 500000 }

-- ------------------------------------------------------------------ fardas / uniforms
-- Componentes: tshirt, torso, decals, arms, pants, shoes, mask, vest, chain, bag / Props: hat, glasses, ears, watch
-- item = -1 num prop tira-o / item -1 on a prop removes it
Config.Outfits = {
    male = {
        { label = 'Patrulha', grade = 0, parts = {
            tshirt = { item = 58, texture = 0 }, torso = { item = 55, texture = 0 }, arms = { item = 30, texture = 0 },
            pants = { item = 24, texture = 0 }, shoes = { item = 51, texture = 0 }, decals = { item = 0, texture = 0 },
            vest = { item = 0, texture = 0 }, hat = { item = -1, texture = 0 },
        } },
        { label = 'Intervenção', grade = 2, parts = {
            tshirt = { item = 15, texture = 0 }, torso = { item = 53, texture = 0 }, arms = { item = 31, texture = 0 },
            pants = { item = 31, texture = 0 }, shoes = { item = 25, texture = 0 }, vest = { item = 7, texture = 0 },
            hat = { item = 117, texture = 0 },
        } },
    },
    female = {
        { label = 'Patrulha', grade = 0, parts = {
            tshirt = { item = 35, texture = 0 }, torso = { item = 48, texture = 0 }, arms = { item = 44, texture = 0 },
            pants = { item = 34, texture = 0 }, shoes = { item = 27, texture = 0 }, decals = { item = 0, texture = 0 },
            vest = { item = 0, texture = 0 }, hat = { item = -1, texture = 0 },
        } },
    },
}

-- ------------------------------------------------------------------ compatibilidade / compatibility
-- Mantém os eventos do qb-policejob que outros scripts usam (contagem de polícias, alertas, "ishandcuffed")
-- Keeps the qb-policejob events other scripts rely on (cop count, alerts, "ishandcuffed")
Config.QBCompat = true

-- Webhook do Discord para registos (multas, prisão, armeiro, apreensões) / Discord log webhook
Config.Webhook = ''

#Integrações (ficheiros abertos)

Tudo o que fala com outros scripts está em server/editable.lua e client/editable.lua, que não são encriptados. Se usas um script diferente, basta mudar a função certa:

FunçãoPara que serve
Hooks.OpenPlayerInventoryRevista (abrir o inventário de outro jogador)
Hooks.OpenStashCacifos de provas
Hooks.SocietyAdd / SocietyRemoveConta do departamento (multas, armeiro)
Hooks.JailPrisão (drs_prison, qb-prison, esx-qalle-jail, pickle_prisons)
Hooks.ImpoundMarcar a viatura como apreendida na tua garagem
Hooks.OpenBossMenu de gestão
Hooks.VehicleOwner / NameByIdentifier / IdentityDados do dono e da identificação
Hooks.WeaponSerialNúmero de série da arma (cápsulas)
ClientHooks.RestoreClothes / GenderRoupa civil e sexo da personagem
ClientHooks.GiveKeys / SetFuelChaves e combustível da viatura de serviço
ClientHooks.OnCuffedAlgo extra quando alguém é algemado

#Para programadores

server
exports.drs_policejob:IsOfficer(src)            -- é polícia?
exports.drs_policejob:IsOnDuty(src)             -- está de serviço?
exports.drs_policejob:GetCopCount()             -- polícias de serviço
exports.drs_policejob:IsCuffed(src)
exports.drs_policejob:Cuff(src, hard)           -- hard = algemas apertadas
exports.drs_policejob:Uncuff(src)
exports.drs_policejob:SetDuty(src, true)
exports.drs_policejob:AddAlcohol(src, 0.3)      -- para scripts de bebidas
exports.drs_policejob:Alert(vector3(0, 0, 0), 'Assalto à loja')
exports.drs_policejob:AddBolo(plate, reason, officer)
exports.drs_policejob:RemoveBolo(plate)
exports.drs_policejob:GetBolo(plate)
exports.drs_policejob:GetBolos()
exports.drs_policejob:GetCallsign(src)

GlobalState.drs_pd_count        -- polícias de serviço
Player(src).state.pdCuffed      -- false | 'soft' | 'hard'
client
exports.drs_policejob:IsCuffed()
exports.drs_policejob:IsEscorted()
exports.drs_policejob:OpenMenu()
exports.drs_policejob:Action('cuff')   -- cuff, escort, search, veh_check, badge, radar, …

Evento de servidor quando alguém entra ou sai de serviço: drs_policejob:dutyChanged (src, onDuty).

#Resolução de problemas

O F6 não abre nada — confirma que o teu emprego está em Config.Departments e que estás de serviço (ou põe Config.RequireDuty = false). Se mudaste a tecla nas definições do GTA, a nova tecla é a que vale.

Não consigo algemar — precisas do item handcuffs no inventário (ou Config.Cuffs.Item = false). Com RequireSurrender = true, a pessoa tem de estar de mãos no ar, de joelhos ou de costas.

Outros scripts deixaram de contar a polícia — mantém Config.QBCompat = true e confirma que o qb-policejob antigo está desligado (os dois ao mesmo tempo entram em conflito).

A prisão não funciona — o Hooks.Jail procura o drs_prison, qb-prison, esx-qalle-jail ou pickle_prisons. Se usas outro, adapta essa função no server/editable.lua.

O radar não liga — só funciona nas viaturas de Config.Radar.Classes (18 = emergência) ou nos modelos de Config.Radar.Models.