DRS Dispatch

v2.1.3 drs_dispatch
Ver na loja

Central de despacho para a polícia, o INEM e os bombeiros: alertas no ecrã com todos os detalhes, blips no mapa, painel de chamadas com o estado das unidades, chamadas de cidadãos pelo 112 e deteção automática de crimes. Usa os mesmos exports do ps-dispatch, por isso os teus scripts de assaltos, droga e mortes continuam a funcionar sem mudar nada. Funciona com Qbox, QBCore e ESX.

#Requisitos

RecursoObrigatório
oxmysqlSim
Qbox, QBCore ou ESXSim (deteção automática)
drs_mdtNão — mostra as chamadas dentro do MDT

#Instalação

  1. Desliga o ps-dispatch (ou outro dispatch). O drs_dispatch tem provide 'ps-dispatch': os scripts que chamam exports['ps-dispatch']:... passam a falar com ele.
  2. Coloca drs_dispatch em resources.
  3. Adiciona ao server.cfg, depois do oxmysql e da framework, e antes dos scripts que enviam alertas:
server.cfg
ensure oxmysql
ensure qb-core
ensure drs_dispatch
# depois: assaltos, droga, EMS, etc.
  1. Ajusta os grupos (Config.Groups), os comandos do 112 e as zonas sem alertas no config.lua.

#Teclas e comandos

Tecla / comandoO que faz
GResponde ao alerta no ecrã: marca o GPS e entra na chamada
← / →Vê o alerta anterior / seguinte
JAbre o painel de despacho (chamadas, unidades, zonas)
/112 mensagemCidadão liga para a polícia
/112a mensagemChamada anónima para a polícia
/inem mensagemCidadão liga para o INEM

Cada jogador pode mudar as teclas em Definições → Atalhos → FiveM. Os comandos de chamada estão em Config.Commands.

#Quem recebe os alertas

Os alertas vão para grupos de empregos. Cada alerta diz os grupos que recebem (jobs = { 'leo' }); também podes pôr o nome de um emprego diretamente.

config.lua
Config.Groups = {
    leo  = { 'police', 'sheriff', 'bcso', 'sasp' },
    ems  = { 'ambulance', 'ems' },
    fire = { 'fire' },
}
Config.RequireDuty = true   -- só recebe quem está de serviço

#Funcionalidades

#Alertas no ecrã

  • Código (ex.: 10-90), título, rua, sexo, viatura, matrícula, cor, direção e arma, conforme o alerta.
  • Cor por prioridade: 1 urgente (vermelho, a piscar), 2 normal, 3 baixa.
  • Barra de tempo (Config.Hud.Seconds), som e até Config.Hud.Max alertas visíveis ao mesmo tempo.

#Mapa

  • Blip e círculo de raio por alerta, a piscar nos urgentes. Tempo no mapa em Config.BlipMinutes ou em length de cada alerta.
  • Zonas: os agentes (cargo Config.Zones.MinGrade ou superior) marcam perímetros no mapa com cor e raio, visíveis para todas as unidades. Desaparecem ao fim de Config.Zones.Minutes.

#Painel de despacho (J)

  • Lista de chamadas com filtros: todas, urgentes e as minhas.
  • Unidades em cada chamada.
  • Estado de cada agente: disponível, patrulha, ocupado ou pausa (Config.Statuses).
  • Responder ao cidadão que ligou.
  • Guarda até Config.MaxCalls chamadas; cada uma sai do painel ao fim de Config.CallMinutes.

#Chamadas de cidadãos

/112, /112a (anónimo) e /inem, com o nome e o telefone de quem ligou (ou anónimo). Opcionalmente, é preciso ter um telemóvel (Config.CallItem = 'phone'). Há um tempo de espera entre chamadas (Config.CallCooldown) e as chamadas podem ir para um webhook do Discord.

#Deteção automática

Em Config.Detect ligas ou desligas: disparos (a pé e de viatura), lutas, carjacking, tentativa de furto de viatura e explosões.

  • Os polícias de serviço não geram alertas (IgnoreOfficers).
  • Com RequireWitness = true, tiros e lutas só dão alerta se um NPC tiver visto.
  • Armas que não contam em Config.WeaponWhitelist (taser, extintor…).
  • Zonas sem alertas (Config.NoDispatchZones, ex.: carreiras de tiro) e zonas de caça (Config.HuntingZones), que dão "caça" em vez de "tiros".

#Alertas prontos

Mais de 30 alertas em Config.Alerts. Cada nome é também um export no cliente, igual ao do ps-dispatch:

TipoAlertas
Crime na ruaShooting, VehicleShooting, Fight, CarJacking, VehicleTheft, SpeedingVehicle, SuspiciousActivity, DrugSale, Explosion, Hunting
AssaltosStoreRobbery, HouseRobbery, FleecaBankRobbery, PaletoBankRobbery, PacificBankRobbery, VangelicoRobbery, ArtGalleryRobbery, HumaneRobbery, TrainRobbery, VanRobbery, UndergroundRobbery, DrugBoatRobbery, UnionRobbery, YachtHeist, BobcatSecurityHeist, SignRobbery, CarBoosting, PrisonBreak
EmergênciaInjuriedPerson, DeceasedPerson, OfficerDown, OfficerBackup, OfficerInDistress, EmsDown

Cada alerta tem estes campos:

config.lua
StoreRobbery = {
    code = '10-90',            -- código mostrado
    icon = 'fas fa-store',     -- ícone Font Awesome
    priority = 2,              -- 1 urgente | 2 normal | 3 baixa
    sprite = 52, color = 60,   -- blip
    radius = 0.0,              -- círculo à volta (0 = nenhum)
    length = 3,                -- minutos no mapa
    jobs = { 'leo' },          -- quem recebe
    gender = true,             -- junta o sexo do suspeito
    vehicle = false,           -- junta a viatura
    weapon = false,            -- junta a arma
},

Para criar um alerta novo, basta acrescentar uma entrada: passa logo a existir exports.drs_dispatch:NomeDoAlerta() no cliente.

#Opções principais

OpçãoPor defeitoDescrição
Config.Locale'pt'Idioma: pt ou en
Config.RequireDutytrueSó recebe quem está de serviço
Config.MaxCalls60Chamadas guardadas no painel
Config.CallMinutes45Minutos até a chamada sair do painel
Config.BlipMinutes3Minutos do blip no mapa
Config.KeysG / J / ← / →Teclas por defeito
Config.Hud.Seconds9Segundos do alerta no ecrã
Config.CallItemfalseItem preciso para ligar para o 112
Config.Detect.RequireWitnessfalseTiros e lutas precisam de testemunha
Config.Zones.EnabledtruePerímetros no mapa
Config.Webhook''Webhook do Discord para as chamadas

#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'
Config.UseOxNotify = false
Config.StaffGroups = { 'god', 'admin', 'superadmin' }
Config.Debug = false

-- ------------------------------------------------------------------ quem recebe / who receives
-- Grupos usados nos alertas (jobs = { 'leo' }). Também se pode pôr o nome de um emprego diretamente.
-- Groups used by alerts (jobs = { 'leo' }). A job name can also be used directly.
Config.Groups = {
    leo = { 'police', 'sheriff', 'bcso', 'sasp' },
    ems = { 'ambulance', 'ems' },
    fire = { 'fire' },
}
-- Só recebe quem está de serviço / Only on-duty players receive alerts
Config.RequireDuty = true

-- ------------------------------------------------------------------ chamadas / calls
Config.MaxCalls = 60            -- chamadas guardadas no painel / calls kept in the panel
Config.CallMinutes = 45         -- depois disto a chamada sai do painel / calls leave the panel after this
Config.BlipMinutes = 3          -- tempo do blip no mapa (cada alerta pode mudar) / map blip time (per alert override: length)

-- ------------------------------------------------------------------ teclas / keys (o jogador muda nas definições do GTA)
Config.Keys = {
    Respond = 'G',              -- marcar GPS e responder ao último alerta / waypoint + respond to the last alert
    Panel = 'J',                -- abrir o painel de despacho / open the dispatch panel
    Prev = 'LEFT',              -- alerta anterior no ecrã / previous alert on screen
    Next = 'RIGHT',             -- alerta seguinte no ecrã / next alert on screen
}

-- ------------------------------------------------------------------ ecrã / screen
Config.Hud = {
    Seconds = 9,                -- quanto tempo o alerta fica no ecrã / how long an alert stays on screen
    Max = 3,                    -- alertas visíveis ao mesmo tempo / alerts visible at once
    Sound = true,
}

-- ------------------------------------------------------------------ chamadas de cidadãos / citizen calls
-- /112 mensagem -> polícia; /112a mensagem -> anónimo; /inem mensagem -> EMS
Config.Commands = {
    { name = '112',  jobs = { 'leo' }, anonymous = false },
    { name = '112a', jobs = { 'leo' }, anonymous = true },
    { name = 'inem', jobs = { 'ems' }, anonymous = false },
}
Config.CallCooldown = 30        -- segundos entre chamadas do mesmo jogador / seconds between calls of the same player
-- Item preciso para ligar (false = nenhum). Ex.: 'phone' / Item needed to call (false = none)
Config.CallItem = false

-- ------------------------------------------------------------------ deteção automática / automatic detection
Config.Detect = {
    Shooting = true,            -- tiros (a pé ou num veículo) / gunshots
    Fight = true,               -- lutas / melee fights
    CarJacking = true,          -- tirar alguém do carro / pulling someone out of a car
    VehicleTheft = true,        -- tentar abrir um carro trancado / trying to open a locked car
    Explosion = true,           -- explosões (lado do servidor) / explosions (server side)
    Cooldown = 25,              -- segundos entre alertas do mesmo tipo / seconds between alerts of the same type
    -- Os polícias de serviço não geram alertas / On-duty police don't trigger alerts
    IgnoreOfficers = true,
    -- Precisa de uma testemunha (NPC que viu) para tiros e lutas / Needs an NPC witness for shots and fights
    RequireWitness = false,
}

-- Armas que não geram alertas de tiros / Weapons that don't trigger gunshot alerts
Config.WeaponWhitelist = {
    'WEAPON_STUNGUN', 'WEAPON_SNOWBALL', 'WEAPON_BALL', 'WEAPON_FLARE', 'WEAPON_FIREEXTINGUISHER', 'WEAPON_PETROLCAN',
}

-- Zonas sem alertas de tiros (carreiras de tiro, etc.) / No gunshot alerts here (shooting ranges, etc.)
Config.NoDispatchZones = {
    { coords = vector3(13.53, -1097.92, 29.83), radius = 25.0 },   -- Ammu-Nation (centro)
    { coords = vector3(821.36, -2163.58, 29.62), radius = 25.0 },  -- Ammu-Nation (Cypress Flats)
}
-- Zonas de caça (dá o alerta "caça" em vez de "tiros") / Hunting zones (sends "hunting" instead of "shots fired")
Config.HuntingZones = {
    { coords = vector3(-1339.0, 4618.0, 150.0), radius = 900.0 },
}

-- ------------------------------------------------------------------ zonas no mapa / map zones
-- Os agentes podem marcar perímetros no mapa para todas as unidades / Officers can mark perimeters for all units
Config.Zones = {
    Enabled = true,
    MinGrade = 1,               -- cargo mínimo / minimum grade
    Minutes = 30,               -- a zona desaparece sozinha / the zone expires
    MaxRadius = 400.0,
    Colors = { red = 1, blue = 3, green = 2, yellow = 5, orange = 47, purple = 27 },
}

-- Estados que o agente pode escolher / Unit statuses
Config.Statuses = {
    { id = 'available', color = '#4ade80' },
    { id = 'patrol',    color = '#60a5fa' },
    { id = 'busy',      color = '#fbbf24' },
    { id = 'break',     color = '#a1a1aa' },
}

-- ------------------------------------------------------------------ alertas / alerts
-- Nome = export no cliente (compatível com o ps-dispatch: exports['ps-dispatch']:Shooting()).
-- Name = client export (compatible with ps-dispatch: exports['ps-dispatch']:Shooting()).
-- priority: 1 = urgente (vermelho, pisca) | 2 = normal | 3 = baixa
-- sprite/color/scale: blip | radius: círculo à volta (0 = nenhum) | length: minutos no mapa
-- vehicle/gender/weapon: junta esses detalhes / adds those details
Config.Alerts = {
    Shooting            = { code = '10-11', icon = 'fas fa-gun',              priority = 2, sprite = 110, color = 1,  jobs = { 'leo' }, gender = true, weapon = true },
    VehicleShooting     = { code = '10-60', icon = 'fas fa-gun',              priority = 2, sprite = 229, color = 1,  jobs = { 'leo' }, vehicle = true, weapon = true },
    Hunting             = { code = '10-13', icon = 'fas fa-crosshairs',       priority = 3, sprite = 141, color = 2,  jobs = { 'leo' } },
    Fight               = { code = '10-10', icon = 'fas fa-hand-fist',        priority = 3, sprite = 685, color = 69, jobs = { 'leo' }, gender = true },
    CarJacking          = { code = '10-35', icon = 'fas fa-car',              priority = 2, sprite = 595, color = 60, jobs = { 'leo' }, vehicle = true, gender = true },
    VehicleTheft        = { code = '10-35', icon = 'fas fa-car-burst',        priority = 3, sprite = 595, color = 60, jobs = { 'leo' }, vehicle = true },
    SpeedingVehicle     = { code = '10-11', icon = 'fas fa-gauge-high',       priority = 3, sprite = 326, color = 84, jobs = { 'leo' }, vehicle = true },
    Explosion           = { code = '10-80', icon = 'fas fa-fire',             priority = 1, sprite = 436, color = 1,  jobs = { 'leo', 'ems' }, radius = 60.0 },
    SuspiciousActivity  = { code = '10-66', icon = 'fas fa-eye',              priority = 3, sprite = 66,  color = 37, jobs = { 'leo' }, gender = true },
    DrugSale            = { code = '10-13', icon = 'fas fa-cannabis',         priority = 3, sprite = 501, color = 25, jobs = { 'leo' }, gender = true },
    PrisonBreak         = { code = '10-90', icon = 'fas fa-person-running',   priority = 1, sprite = 189, color = 59, jobs = { 'leo' }, radius = 120.0 },
    StoreRobbery        = { code = '10-90', icon = 'fas fa-store',            priority = 2, sprite = 52,  color = 60, jobs = { 'leo' }, gender = true },
    HouseRobbery        = { code = '10-90', icon = 'fas fa-house',            priority = 2, sprite = 40,  color = 60, jobs = { 'leo' }, gender = true },
    FleecaBankRobbery   = { code = '10-90', icon = 'fas fa-vault',            priority = 1, sprite = 500, color = 60, jobs = { 'leo' }, radius = 60.0 },
    PaletoBankRobbery   = { code = '10-90', icon = 'fas fa-vault',            priority = 1, sprite = 500, color = 60, jobs = { 'leo' }, radius = 60.0 },
    PacificBankRobbery  = { code = '10-90', icon = 'fas fa-vault',            priority = 1, sprite = 500, color = 60, jobs = { 'leo' }, radius = 80.0 },
    VangelicoRobbery    = { code = '10-90', icon = 'fas fa-gem',              priority = 1, sprite = 617, color = 60, jobs = { 'leo' }, radius = 60.0 },
    ArtGalleryRobbery   = { code = '10-90', icon = 'fas fa-palette',          priority = 1, sprite = 269, color = 60, jobs = { 'leo' }, radius = 60.0 },
    HumaneRobbery       = { code = '10-90', icon = 'fas fa-flask',            priority = 1, sprite = 499, color = 60, jobs = { 'leo' }, radius = 80.0 },
    TrainRobbery        = { code = '10-90', icon = 'fas fa-train',            priority = 1, sprite = 667, color = 60, jobs = { 'leo' }, radius = 80.0 },
    VanRobbery          = { code = '10-90', icon = 'fas fa-van-shuttle',      priority = 1, sprite = 67,  color = 60, jobs = { 'leo' }, radius = 60.0 },
    UndergroundRobbery  = { code = '10-90', icon = 'fas fa-dungeon',          priority = 1, sprite = 486, color = 60, jobs = { 'leo' }, radius = 60.0 },
    DrugBoatRobbery     = { code = '10-90', icon = 'fas fa-ship',             priority = 1, sprite = 427, color = 60, jobs = { 'leo' }, radius = 80.0 },
    UnionRobbery        = { code = '10-90', icon = 'fas fa-building-columns', priority = 1, sprite = 500, color = 60, jobs = { 'leo' }, radius = 80.0 },
    YachtHeist          = { code = '10-90', icon = 'fas fa-anchor',           priority = 1, sprite = 455, color = 60, jobs = { 'leo' }, radius = 80.0 },
    BobcatSecurityHeist = { code = '10-90', icon = 'fas fa-shield-halved',    priority = 1, sprite = 487, color = 60, jobs = { 'leo' }, radius = 80.0 },
    SignRobbery         = { code = '10-62', icon = 'fas fa-sign-hanging',     priority = 3, sprite = 358, color = 60, jobs = { 'leo' }, gender = true },
    CarBoosting         = { code = '10-35', icon = 'fas fa-car',              priority = 2, sprite = 595, color = 60, jobs = { 'leo' }, vehicle = true },
    InjuriedPerson      = { code = '10-69', icon = 'fas fa-user-injured',     priority = 2, sprite = 153, color = 1,  jobs = { 'ems' }, gender = true },
    DeceasedPerson      = { code = '10-69', icon = 'fas fa-skull',            priority = 2, sprite = 310, color = 1,  jobs = { 'ems', 'leo' }, gender = true },
    OfficerDown         = { code = '10-99', icon = 'fas fa-skull',            priority = 1, sprite = 526, color = 1,  jobs = { 'leo', 'ems' }, officer = true },
    OfficerBackup       = { code = '10-32', icon = 'fas fa-people-group',     priority = 1, sprite = 526, color = 3,  jobs = { 'leo' }, officer = true },
    OfficerInDistress   = { code = '10-13', icon = 'fas fa-triangle-exclamation', priority = 1, sprite = 526, color = 1, jobs = { 'leo' }, officer = true },
    EmsDown             = { code = '10-99', icon = 'fas fa-truck-medical',    priority = 1, sprite = 526, color = 3,  jobs = { 'leo', 'ems' }, officer = true },
    Call                = { code = '112',   icon = 'fas fa-phone',            priority = 2, sprite = 480, color = 3,  jobs = { 'leo' } },
}

-- Webhook do Discord para registar as chamadas do 112 / Discord webhook for 112 calls
Config.Webhook = ''

#Integrações (ficheiro aberto)

O server/editable.lua não é encriptado:

FunçãoPara que serve
Hooks.OnDutySaber se alguém está de serviço
Hooks.CallsignIndicativo do agente (vem do drs_policejob se existir)
Hooks.PhoneNúmero de telefone de quem liga para o 112
Hooks.OnAlertCorre a cada alerta enviado (ex.: mandar para outro sistema)

#Para programadores

Cliente — os mesmos do ps-dispatch (funcionam com exports['ps-dispatch'] ou exports.drs_dispatch):

client
exports['ps-dispatch']:StoreRobbery()
exports['ps-dispatch']:Shooting()
exports['ps-dispatch']:OfficerDown()

exports['ps-dispatch']:CustomAlert({
    message = 'Alarme ativado',
    dispatchCode = '10-90',
    code = '10-90',
    icon = 'fas fa-bell',
    priority = 1,
    coords = GetEntityCoords(PlayerPedId()),
    sprite = 161, color = 1, scale = 1.2, length = 3,
    jobs = { 'leo' },
})

Servidor:

server
exports.drs_dispatch:SendAlert({ coords = coords, message = 'Alarme ativado', code = '10-90', icon = 'fas fa-bell', priority = 1, jobs = { 'leo' }, sprite = 161, color = 1 })
exports.drs_dispatch:SendPreset('StoreRobbery', coords)
exports.drs_dispatch:GetDispatchCalls()   -- lista de chamadas ativas

-- compatibilidade com o ps-dispatch
TriggerEvent('ps-dispatch:server:notify', data)

#Segurança

Os alertas pré-definidos usam a posição do jogador lida no servidor. Os alertas livres enviados por um cliente só são aceites perto do próprio jogador, todos os textos são limpos e limitados, e há limites de frequência por jogador. Um cliente modificado não consegue inventar alertas noutro sítio do mapa.

#Resolução de problemas

Não recebo alertas — o teu emprego tem de estar num grupo de Config.Groups e tens de estar de serviço (Config.RequireDuty). Os alertas de tiros não aparecem se fores tu a disparar de serviço (IgnoreOfficers).

Os scripts de assaltos não dão alerta — confirma que o ps-dispatch está desligado e que o drs_dispatch arranca antes desses scripts no server.cfg.

Alertas de tiros na carreira de tiro — acrescenta a zona em Config.NoDispatchZones.

O /112 não faz nada — há um tempo de espera entre chamadas (Config.CallCooldown) e, se Config.CallItem estiver definido, precisas desse item no inventário.