DRS MDT

v2.5.4 drs_mdt
Ver na loja

Tablet da polícia: fichas de cidadãos com fotografia, ocorrências com código penal, mandados aprovados pelo tribunal, viaturas com localização, mapa com todas as unidades, câmaras de segurança e chat entre agentes. Permissões por cargo e toda a lógica validada no servidor. Funciona com Qbox, QBCore e ESX.

Dica

O MDT funciona sozinho, mas ganha páginas extra com os outros scripts DRS da polícia: Police Job (BOLO, provas do laboratório, indicativos e horas de serviço), Dispatch (chamadas e estado das unidades) e Prison (pena aplicada pela ocorrência, reclusos e registo prisional). Sem eles, essas páginas simplesmente não aparecem.

#Requisitos

RecursoObrigatório
oxmysqlSim
Qbox, QBCore ou ESXSim (deteção automática)
screenshot-basicNão — para tirar fotografias em jogo
drs_policejobNão — BOLO, provas, indicativos e horas de serviço
drs_dispatchNão — página de chamadas
drs_prisonNão — prisão pela ocorrência e registo prisional

#Instalação

  1. Coloca drs_mdt em resources.
  2. Adiciona ao server.cfg, depois do oxmysql e da framework (e dos outros scripts DRS da polícia, se os tiveres):
server.cfg
ensure oxmysql
ensure qb-core
ensure screenshot-basic
ensure drs_policejob
ensure drs_dispatch
ensure drs_prison
ensure drs_mdt
  1. As tabelas (drs_mdt_*) são criadas sozinhas no primeiro arranque. As multas usam a tabela drs_pd_fines, a mesma do drs_policejob.
  2. Ajusta os departamentos e as permissões em Config.Jobs e o código penal em Config.Charges.
  3. Opcional: para mostrar a foto do Discord de cada agente, põe o token de um bot no server.cfg:
server.cfg
set drs_discord_bot_token "O_TEU_TOKEN"

#Abrir o MDT

FormaComo
Comando/mdt (muda em Config.Command; false desliga)
TeclaEm Config.Key, ex.: 'F9'. Cada jogador pode mudá-la em Definições → Atalhos → FiveM
ItemEm Config.Item, ex.: 'mdt' — usar o item abre o tablet
Só no carroConfig.OnlyInVehicle = true só deixa abrir dentro de um veículo de emergência

Com Config.RequireDuty = true, só abre de serviço. Os juízes (Config.Warrants.Court.Jobs) abrem o MDT em modo tribunal.

#Departamentos e permissões

Cada emprego em Config.Jobs tem o seu nome, cor e permissões. Em perms defines o cargo mínimo de cada ação (o que faltar fica a 0):

PermissãoO que desbloqueia
report_deleteApagar ocorrências
report_edit_allEditar ocorrências de outros agentes
warrant / warrant_closePedir mandados / marcá-los como cumpridos
licenseVer os botões das licenças (cada licença tem ainda o seu cargo em Config.Licenses)
fineMultar pelo MDT
processAplicar a pena de uma ocorrência (multa + prisão)
boloMarcar viaturas como procuradas
noticePublicar avisos no painel
profile_editEditar notas, foto e etiquetas das pessoas
evidence_add / evidence_deleteRegistar / apagar provas
map / cams / chat / rosterMapa, câmaras, chat e efetivo
charges_editEditar o código penal ('boss' = só o patrão)

#Funcionalidades

#Painel

Cartão de boas-vindas com indicativo, cargo, horas de serviço do dia e a foto do Discord. Ações rápidas (nova ocorrência, procurar pessoa, ver matrícula, pedir mandado), estatísticas (unidades de serviço, chamadas, mandados, pedidos ao tribunal, ocorrências e reclusos), mini-mapa em direto, avisos do departamento e as últimas ocorrências e mandados.

#Mapa em direto

Mapa real do GTA com os agentes de serviço e as chamadas ativas, atualizado a cada Config.Map.Refresh segundos. Clica num agente para ver o nome e o estado e marcar o GPS até ele.

#Pessoas

  • Pesquisa por nome, apelido ou ID.
  • Fotografia: mugshot tirado em jogo (screenshot-basic), link, ou automático quando a polícia vê a identificação ou aplica uma pena (Config.Photo.Auto). A imagem fica na base de dados (~25 KB).
  • Dados pessoais, emprego, telefone e impressão digital.
  • Etiquetas (Config.Tags: perigoso, armado, gang…) e notas.
  • Licenças (Config.Licenses), cada uma com o seu cargo mínimo para dar ou tirar. No QBCore/Qbox usa metadata.licences; no ESX, a tabela user_licenses.
  • Viaturas, propriedades (casas, apartamentos, motel), multas, multas pendentes e registo prisional.
  • Multar pelo MDT. Com Config.Fines.Offline = true, quem está offline fica com a multa pendente e paga quando entrar.

#Ocorrências

  1. Cria a ocorrência: título, tipo (Config.ReportTypes), local (botão "aqui"), descrição e agentes envolvidos.
  2. Junta os suspeitos e as acusações do código penal: a multa e os meses são somados sozinhos. Podes ajustar a pena à mão.
  3. Aplicar pena: multa + prisão com um clique. Com Config.Process.RequireNearby, o suspeito tem de estar a menos de Distance metros.
  4. Junta imagens e vídeos (imgur, Discord, YouTube, Medal, mp4 — até Config.MaxMedia) e as provas registadas ou do laboratório.

Depois de processado, um suspeito já não pode ser alterado.

#Mandados pelo tribunal

Com Config.Warrants.Court.Enabled = true, a polícia pede o mandado e o juiz aprova ou recusa no próprio MDT, com uma nota. Os mandados aprovados valem Config.Warrants.Days dias e quem pediu é avisado. Separadores Pendentes / Ativos / Histórico. Sem tribunal, a polícia emite os mandados diretamente. Com PoliceApproveGrade, um cargo da polícia pode aprovar quando não há juízes.

#Viaturas

Viaturas procuradas (BOLO, roubadas e donos com mandado) logo à entrada. Imagem do modelo, matrícula, dono com foto e carta de condução, estado, garagem, apreensão, quilómetros, seguro, combustível, motor e carroçaria. Localização atual no mapa se a viatura estiver na rua, fotografias tiradas em jogo (até Config.MaxVehiclePhotos) e histórico de ocorrências e multas de radar. O radar do carro de patrulha avisa de viaturas roubadas e de donos com mandado.

#Provas, chamadas, câmaras e chat

  • Provas com tipo (Config.EvidenceTypes), descrição, fotos e vídeos.
  • Chamadas do drs_dispatch, com mapa, "Responder" e "Criar ocorrência".
  • Câmaras de segurança com imagem real do jogo (Config.Cameras). Setas rodam, rato/scroll aproxima, ←/→ troca de câmara e Backspace sai.
  • Chat entre agentes.
  • Código penal editável no próprio MDT (quem tiver charges_edit).
  • Reclusos e efetivo com horas de serviço.

#Código penal

Cada categoria tem uma cor e uma lista de crimes com multa e meses (0 = sem prisão):

config.lua
Config.Charges = {
    {
        category = 'Trânsito', color = '#60a5fa',
        items = {
            { id = 't1', label = 'Excesso de velocidade', fine = 300, months = 0 },
            { id = 't3', label = 'Condução perigosa', fine = 1500, months = 5 },
        },
    },
}

Os totais são sempre calculados no servidor a partir desta lista. Os id têm de ser únicos.

#Câmaras de segurança

config.lua
Config.Cameras = {
    { label = 'Fleeca — Praça Legion', coords = vector3(160.0, -1055.0, 36.0), heading = 36.0, pitch = -20.0 },
}

coords é a posição da câmara, heading para onde olha e pitch a inclinação (negativo = para baixo).

#Opções principais

OpçãoPor defeitoDescrição
Config.Locale'pt'Idioma: pt ou en
Config.Command'mdt'Comando para abrir
Config.Key''Tecla para abrir (vazio = sem tecla)
Config.ItemfalseItem que abre o MDT
Config.OnlyInVehiclefalseSó dentro de um veículo de emergência
Config.RequireDutytrueSó de serviço
Config.Process.MaxMonths300Pena máxima
Config.Fines.OfflinetrueMultas a jogadores offline ficam pendentes
Config.Warrants.Days7Validade dos mandados
Config.Warrants.Court.EnabledtrueMandados precisam do tribunal
Config.Map.EnabledtrueMapa em direto
Config.Photo.AutotrueMugshot automático
Config.Webhook''Webhook do Discord

#Configuração completa

config.lua
Config = {}

-- Idioma / Language: 'pt' ou 'en'
Config.Locale = 'pt'
Config.Framework = 'auto'
Config.UseOxNotify = false
Config.StaffGroups = { 'god', 'admin', 'superadmin' }
Config.Debug = false
Config.Currency = '€'

-- ------------------------------------------------------------------ abrir / open
Config.Command = 'mdt'                -- false = sem comando / no command
Config.Key = ''                       -- ex.: 'F9' ('' = sem tecla) / e.g. 'F9' ('' = no key)
Config.Item = false                   -- ex.: 'mdt' — usar o item abre o MDT / using the item opens the MDT
Config.OnlyInVehicle = false          -- só dentro de um veículo de emergência / only inside an emergency vehicle
Config.RequireDuty = true
Config.TabletAnim = true

-- ------------------------------------------------------------------ departamentos / departments
-- perms = cargo mínimo para cada ação (as que faltam ficam a 0)
-- perms = minimum grade for each action (anything missing is 0)
Config.Jobs = {
    police = {
        label = 'PSP', title = 'Polícia de Segurança Pública', color = '#3b82f6',
        perms = {
            report_delete = 3,        -- apagar ocorrências / delete reports
            report_edit_all = 2,      -- editar ocorrências de outros / edit other officers' reports
            warrant = 1,              -- pedir mandados ao tribunal (ou emitir, sem tribunal) / request (or issue) warrants
            warrant_close = 1,        -- marcar como cumprido / mark served
            license = 2,              -- ver os botões das licenças (cada licença tem ainda o seu cargo em Config.Licenses)
                                      -- license buttons (each license also has its own grade in Config.Licenses)
            fine = 0,                 -- multar pelo MDT / fine from the MDT
            process = 0,              -- aplicar pena da ocorrência (multa + prisão) / apply report sentence
            bolo = 1,
            notice = 3,               -- avisos no painel / dashboard notices
            profile_edit = 0,         -- notas, foto e etiquetas / notes, photo and tags
            evidence_add = 0,         -- registar provas / add evidence
            evidence_delete = 3,
            map = 0,                  -- ver o mapa com os agentes / see the live map
            cams = 0,                 -- câmaras de segurança / security cameras
            chat = 0,                 -- chat entre agentes / officer chat
            charges_edit = 'boss',    -- editar o código penal: 'boss' = só o patrão / edit the penal code: 'boss' = boss only
            roster = 0,
        },
    },
    -- sheriff = { label = 'GNR', title = 'Guarda Nacional Republicana', color = '#16a34a', perms = { notice = 4 } },
}

-- ------------------------------------------------------------------ processar / processing
Config.Process = {
    RequireNearby = true,             -- o suspeito tem de estar perto do agente / suspect must be near the officer
    Distance = 15.0,
    MaxMonths = 300,
}

-- ------------------------------------------------------------------ multas / fines
Config.Fines = {
    Account = 'bank',
    AllowDebt = true,                 -- fica com saldo negativo se não tiver dinheiro / may go negative
    ToSociety = 1.0,                  -- parte para a conta do departamento (0-1) / share to the society
    -- Offline: a multa fica pendente e é cobrada quando entrar / Offline: charged when the player joins
    Offline = true,
}

-- ------------------------------------------------------------------ licenças / licenses
-- QBCore/Qbox: metadata.licences | ESX: tabela user_licenses
-- grade = cargo mínimo para dar ou tirar ESTA licença / minimum grade to grant or revoke THIS license
Config.Licenses = {
    { id = 'driver',   label = 'Carta de condução',              grade = 2 },
    { id = 'weapon',   label = 'Licença de uso e porte de arma', grade = 4 },
    { id = 'business', label = 'Licença comercial',              grade = 3 },
    { id = 'pilot',    label = 'Licença de piloto',              grade = 3 },
}

-- ------------------------------------------------------------------ mandados / warrants
Config.Warrants = {
    Days = 7,                         -- validade depois de aprovado / validity once approved
    -- Com o tribunal ligado a polícia só PEDE o mandado; o tribunal aprova ou recusa no próprio MDT.
    -- With the court on, the police only REQUEST the warrant; the court approves or rejects it in the MDT.
    Court = {
        Enabled = true,
        Name = 'Tribunal Judicial de Los Santos',
        -- Empregos do tribunal = cargo mínimo. Abrem o MDT em modo tribunal (pessoas, ocorrências, mandados).
        -- Court jobs = minimum grade. They open the MDT in court mode (people, reports, warrants).
        Jobs = { judge = 0 },
        Color = '#a855f7',
    },
    -- Cargo da polícia que também pode aprovar pedidos quando não há juízes (false = só o tribunal)
    -- Police grade that may also approve requests when there are no judges (false = court only)
    PoliceApproveGrade = false,
}

-- ------------------------------------------------------------------ mapa / live map
-- Mapa do GTA com os agentes de serviço e as chamadas / GTA map with on-duty officers and calls
Config.Map = {
    Enabled = true,
    -- Mosaicos do mapa ({style} = game | render | print) / Map tiles
    TileUrl = 'https://s.rsg.sc/sc/images/games/GTAV/map/{style}/{z}/{x}/{y}.jpg',
    Style = 'game',
    Refresh = 3,                      -- segundos / seconds
}

-- ------------------------------------------------------------------ fotografias / photos
-- "Tirar foto" no perfil usa o screenshot-basic e guarda a imagem na base de dados (pequena, ~25 KB)
-- "Take photo" in the profile uses screenshot-basic and stores the image in the database (small, ~25 KB)
-- Auto = mugshot automático quando a polícia vê a identificação (se ainda não houver foto) e quando aplica uma pena
-- Auto = automatic mugshot when police check the ID (if there is no photo yet) and when a sentence is applied
Config.Photo = { Resource = 'screenshot-basic', Size = 320, Quality = 0.82, Auto = true }

-- ------------------------------------------------------------------ provas / evidence
Config.EvidenceTypes = {
    { id = 'photo',     label = 'Fotografia',  icon = 'fa-camera' },
    { id = 'video',     label = 'Vídeo',       icon = 'fa-video' },
    { id = 'document',  label = 'Documento',   icon = 'fa-file-lines' },
    { id = 'object',    label = 'Objeto',      icon = 'fa-box' },
    { id = 'statement', label = 'Depoimento',  icon = 'fa-comment-dots' },
    { id = 'other',     label = 'Outro',       icon = 'fa-circle-question' },
}
-- Máximo de links (imagens/vídeos) por ocorrência ou prova / Max links per report or evidence
Config.MaxMedia = 12
-- Fotografias por viatura / Photos per vehicle
Config.MaxVehiclePhotos = 6
-- Imagem do modelo da viatura. Ordem: VehicleImageOverrides → imagem do stand (real_vehicleshop) → docs do FiveM (só carros base)
-- Vehicle model picture. Order: VehicleImageOverrides → car dealer picture (real_vehicleshop) → FiveM docs (base cars only)
Config.VehicleImages = 'https://docs.fivem.net/vehicles/{model}.webp'
Config.VehicleImageOverrides = {
    -- ['sian'] = 'https://i.imgur.com/xxxx.png',
}

-- Foto do Discord de cada jogador (como no drs_banking): set drs_discord_bot_token "TOKEN" no server.cfg
-- Discord picture of each player (like drs_banking): set drs_discord_bot_token "TOKEN" in server.cfg
Config.DiscordTokenConvar = 'drs_discord_bot_token'

-- ------------------------------------------------------------------ câmaras / security cameras
-- coords = posição da câmara | heading = para onde olha | pitch = inclinação (negativo = para baixo)
-- Durante a câmara: setas rodam, rato/scroll aproxima, ←/→ troca de câmara, BACKSPACE sai
Config.Cameras = {
    { label = 'Loja 24/7 — Strawberry',      coords = vector3(37.00, -1365.00, 36.00),   heading = 31.0,  pitch = -22.0 },
    { label = 'Fleeca — Praça Legion',       coords = vector3(160.00, -1055.00, 36.00),  heading = 36.0,  pitch = -20.0 },
    { label = 'Pacific Standard — Fachada',  coords = vector3(210.00, 190.00, 115.00),   heading = 321.0, pitch = -18.0 },
    { label = 'Vangelico — Rua',             coords = vector3(-650.00, -255.00, 45.00),  heading = 312.0, pitch = -20.0 },
    { label = 'Banco de Paleto — Rua',       coords = vector3(-95.00, 6445.00, 38.00),   heading = 47.0,  pitch = -20.0 },
    { label = 'Hospital Pillbox — Entrada',  coords = vector3(280.00, -600.00, 50.00),   heading = 312.0, pitch = -25.0 },
    { label = 'Aeroporto — Terminal',        coords = vector3(-1060.00, -2760.00, 25.00), heading = 315.0, pitch = -15.0 },
    { label = 'Esquadra — Entrada',          coords = vector3(433.00, -978.50, 33.50),   heading = 90.0,  pitch = -20.0 },
    { label = 'Praça Legion',                coords = vector3(195.00, -915.00, 45.00),   heading = 180.0, pitch = -30.0 },
}

-- ------------------------------------------------------------------ chat
Config.Chat = { MaxMessages = 150, MaxLength = 300 }

-- ------------------------------------------------------------------ etiquetas de perfil / profile tags
Config.Tags = {
    { id = 'dangerous', label = 'Perigoso',        color = '#ef4444' },
    { id = 'armed',     label = 'Armado',          color = '#f97316' },
    { id = 'gang',      label = 'Membro de gang',  color = '#a855f7' },
    { id = 'probation', label = 'Liberdade condicional', color = '#eab308' },
    { id = 'informant', label = 'Informador',      color = '#22c55e' },
    { id = 'mental',    label = 'Instável',        color = '#06b6d4' },
}

-- Tipos de ocorrência / Report types
Config.ReportTypes = {
    { id = 'incident', label = 'Ocorrência' },
    { id = 'arrest',   label = 'Detenção' },
    { id = 'traffic',  label = 'Trânsito' },
    { id = 'investigation', label = 'Investigação' },
    { id = 'other',    label = 'Outro' },
}

-- ------------------------------------------------------------------ código penal / penal code
-- months = meses de prisão (0 = sem prisão) / months in jail (0 = no jail)
Config.Charges = {
    {
        category = 'Trânsito', color = '#60a5fa',
        items = {
            { id = 't1', label = 'Excesso de velocidade (até 30 km/h)',   fine = 300,  months = 0 },
            { id = 't2', label = 'Excesso de velocidade (mais de 30 km/h)', fine = 900, months = 0 },
            { id = 't3', label = 'Condução perigosa',                     fine = 1500, months = 5 },
            { id = 't4', label = 'Condução sem carta',                    fine = 1000, months = 0 },
            { id = 't5', label = 'Condução sob o efeito de álcool',       fine = 2000, months = 10 },
            { id = 't6', label = 'Fuga à polícia',                        fine = 3000, months = 15 },
            { id = 't7', label = 'Estacionamento ilegal',                 fine = 150,  months = 0 },
        },
    },
    {
        category = 'Contra pessoas', color = '#f97316',
        items = {
            { id = 'p1', label = 'Ofensas à integridade física',          fine = 2500, months = 10 },
            { id = 'p2', label = 'Ameaças',                               fine = 1200, months = 5 },
            { id = 'p3', label = 'Sequestro',                             fine = 8000, months = 35 },
            { id = 'p4', label = 'Tentativa de homicídio',                fine = 12000, months = 50 },
            { id = 'p5', label = 'Homicídio',                             fine = 20000, months = 90 },
            { id = 'p6', label = 'Agressão a agente da autoridade',       fine = 5000, months = 25 },
        },
    },
    {
        category = 'Contra o património', color = '#eab308',
        items = {
            { id = 'b1', label = 'Furto',                                 fine = 1500, months = 5 },
            { id = 'b2', label = 'Furto de viatura',                      fine = 3000, months = 12 },
            { id = 'b3', label = 'Roubo a loja',                          fine = 6000, months = 25 },
            { id = 'b4', label = 'Roubo a banco',                         fine = 15000, months = 60 },
            { id = 'b5', label = 'Dano',                                  fine = 1000, months = 0 },
            { id = 'b6', label = 'Invasão de propriedade',                fine = 1500, months = 5 },
        },
    },
    {
        category = 'Armas e droga', color = '#a855f7',
        items = {
            { id = 'd1', label = 'Posse de droga (consumo)',              fine = 800,  months = 0 },
            { id = 'd2', label = 'Tráfico de droga',                      fine = 8000, months = 30 },
            { id = 'd3', label = 'Posse de arma sem licença',             fine = 5000, months = 15 },
            { id = 'd4', label = 'Posse de arma proibida',                fine = 10000, months = 30 },
            { id = 'd5', label = 'Disparo em via pública',                fine = 4000, months = 15 },
        },
    },
    {
        category = 'Ordem pública', color = '#22c55e',
        items = {
            { id = 'o1', label = 'Desobediência',                         fine = 1500, months = 5 },
            { id = 'o2', label = 'Resistência à detenção',                fine = 2500, months = 10 },
            { id = 'o3', label = 'Falsas declarações',                    fine = 2000, months = 5 },
            { id = 'o4', label = 'Uso de máscara em crime',               fine = 1000, months = 5 },
            { id = 'o5', label = 'Evasão da prisão',                      fine = 10000, months = 40 },
        },
    },
}

-- Webhook do Discord (ocorrências, mandados, processamentos) / Discord webhook
Config.Webhook = ''

#Integrações (ficheiro aberto)

O server/editable.lua não é encriptado. Tem as funções que leem e escrevem nos teus scripts: Hooks.SearchPeople, GetPerson, GetLicenses, SetLicense, PersonVehicles, SearchVehicles, VehicleDetails, PersonProperties, ChargeFine, SocietyAdd, Jail, GetBolo, SetBolo, WantedOwnerPlates, Callsign, OnDuty e JobMembers. Se usas uma garagem, casas ou prisão diferentes, adapta a função correspondente.

#Para programadores

server
exports.drs_mdt:AddWarrant(identifier, name, reason, author)   -- cria um mandado ativo
exports.drs_mdt:HasWarrant(identifier)                         -- true / false
exports.drs_mdt:GetWarrants(identifier)                        -- mandados ativos
exports.drs_mdt:Fine(identifier, amount, reason, officer, job) -- offline fica pendente
exports.drs_mdt:GetSummary(identifier)  -- { photo, warrants, reports, fines, tags }
exports.drs_mdt:Mugshot(src, force)     -- tira a foto (force = substitui a atual)
exports.drs_mdt:GetFlaggedPlates()      -- matrículas procuradas
client
exports.drs_mdt:OpenProfile(identifier)   -- abre o MDT na ficha da pessoa

O drs_prison cria mandados sozinho (pelo AddWarrant) quando alguém foge da prisão.

#Resolução de problemas

O /mdt não abre — o teu emprego tem de estar em Config.Jobs (ou nos empregos do tribunal) e tens de estar de serviço. Com Config.OnlyInVehicle = true, entra num veículo de emergência.

Não aparece a página de chamadas / reclusos / BOLO — essas páginas dependem do drs_dispatch, drs_prison e drs_policejob. Confirma que estão a correr e que arrancam antes do drs_mdt.

O botão "Tirar foto" não faz nada — precisas do screenshot-basic a correr.

As fotos do Discord não aparecem — falta o set drs_discord_bot_token no server.cfg, ou o bot não está no teu servidor do Discord.

O mapa aparece cinzento — os mosaicos vêm de Config.Map.TileUrl. Se o endereço deixar de funcionar, troca o Style (game, render ou print) ou usa outro servidor de mosaicos.