MDT
Police, EMS and fire in one MDT. Live CAD, full arrest and incident reports, cases and evidence, cameras and live body cams, and a map of every unit on duty.
Requirements
ox_liboxmysql- Qbox (
qbx_core) or QBCore (qb-core)
ox_target or qb-target is used when you have one, for right-clicking a player to open the MDT or write a citation.
Current release is v3.0.1.
Installation
- Drop the
XS-MDTfolder into your resources. - Add
ensure XS-MDTafterox_lib,oxmysqland your framework. - Put your job names into
Config.Departments. See below. - Start the server.
ensure XS-MDT
The database sets itself up on first start, with the 178-charge penal code already in, and the console says what it made. sql/mdt.sql is there if you would rather run it by hand.
Players open it with F9 and can rebind it in the GTA key bindings.
Departments
Police, EMS and fire each get their own panels. Config.Departments maps your job names to a department, and each department only ever sees its own panels.
Config.Departments = {
police = {
label = 'Police Department',
short = 'PD',
jobs = { 'police', 'sheriff', 'swat', 'statepolice' },
supervisorGrade = 3,
panels = { 'dashboard', 'roster', 'map', 'civilians', ... },
},
ems = { jobs = { 'ambulance', 'ems' }, ... },
fire = { jobs = { 'fire', 'firefighter' }, ... },
}
Job names have to match your framework exactly, including case. Anyone at or above supervisorGrade gets the supervisor tools: Internal Affairs, the Management panel and audit log, editing charges, marking citations paid, revoking licences and releasing evidence.
Reports and cases
Arrests open as a full report with the charges, fine and jail, the write-up, linked incident reports, evidence and cases. Log Arrest has templates for the usual arrest types and saves as you type, so a misclick never loses a report. The arresting officer or a supervisor can edit it later.
Incident reports link to arrests and citations from either side. Click a linked record to open it.
Cases hold a whole job in one place. Search for reports, arrests, citations, warrants, BOLOs, evidence, people, vehicles or guns to attach them, then click anything on the case to open it.
Evidence is booked into the locker with its own number, and every change goes in a custody log. Attach it to a report, an arrest or a case whenever you like.
Licences show on a civilian's profile. Officers grant, suspend and reinstate them, and supervisors revoke. The change lands on the character too, so other scripts agree with the MDT.
The report templates sit at the top of html/js/panels/records.js and incidents.js. Edit them freely.
Cameras and body cams
35 cameras come set up at banks, stores and streets. Add your own in Config.DefaultCameras. Arrow keys pan, scroll zooms and ESC comes back.
Cameras face north until a supervisor aims them. Stand where the camera should be, look at what it should see, and press Aim here on its card.
Body cams let command watch what an on-duty officer's chest camera sees. The officer is told, every viewing goes in the audit log, and a supervisor only sees their own department.
Config.BodyCam.Live = {
Enabled = true,
SupervisorOnly = true,
NotifyOfficer = true,
SameDepartment = true,
}
Your character stays where it is while you watch, and the view closes if you get hurt.
Dispatch
The MDT has its own CAD, and it works with the dispatch script you already run, both ways. Calls made in the MDT pop up in ps-dispatch, qs-dispatch, cd_dispatch, core_dispatch, rcore_dispatch or linden_outlawalert. Alerts from those land on the CAD with a tag saying where they came from, so officers can respond, add notes and close them in the MDT.
Config.DispatchAlerts = 'auto' -- MDT calls go out to every one that is running
Config.DispatchCalls = 'auto' -- and their alerts come in to the CAD
-- { 'ps-dispatch' } for only the ones you name, false for off
-- The MDT stops reporting these itself while your dispatch sends calls in,
-- so a shooting is not on the board twice. {} keeps both.
Config.DispatchCallsReplace = { 'SHOTS_FIRED', 'FIGHT' }
-- Calls from your dispatch that nobody answers clear after this many minutes
Config.DispatchCallsExpire = 30
-- Only if your dispatch folder has a name the MDT can't match
Config.DispatchAlertResources = { ['ps-dispatch'] = 'my-dispatch' }
The Management panel shows each one as alerts out, calls in, or both.
Sending a call from another script
exports['XS-MDT']:CreateDispatchCall({
callType = 'BANK_ROBBERY',
description = 'Silent alarm at the Legion Square Fleeca.',
coords = { x = 149.2, y = -1040.3, z = 29.4 },
street = 'Vespucci Blvd', -- optional
callerName = '911 Caller', -- optional
})
From a client script, TriggerEvent('XS-MDT:client:dispatch:custom', 'ROBBERY', 'Armed robbery in progress.', coords) does the same.
Call types are SHOTS_FIRED, FIGHT, VEHICLE_CRASH, ROBBERY, BANK_ROBBERY, STORE_ROBBERY, TRAFFIC_STOP, MEDICAL, FIRE and SUSPICIOUS. Config.CallRouting decides which departments hear each one.
Fines and jail
Config.FineDeduction = {
Enabled = true,
AutoDeductArrests = true, -- take the fine when the arrest is logged
AutoDeductCitations = false, -- pay-later, unless the officer ticks the box
AutoDeductOnMarkPaid = true, -- take it when a supervisor marks it paid
}
-- 'qb-prison' | 'ps-prison' | false
Config.JailResource = false
Fines and jail only apply to someone who is in the city when the arrest is logged. With Config.JailResource = false the MDT fires XS-MDT:client:jailPlayer on the suspect with the minutes instead, so you can hand it to any jail script.
Other settings
Config.Framework = 'auto' -- or 'qbox' / 'qbcore' if you run both
Config.OpenKey = 'F9'
Config.OnDutyOnly = true -- only opens while on duty
Config.MinGrade = 0 -- 0 = every grade
Config.BootScreen = true -- the start-up screen on first open
Config.VehicleStateFrom = 'auto' -- where a car's parked or out status is read from
Config.Licences = {
Enabled = true,
WriteBackToMetadata = true, -- the licence changes on the character too
RevokeRequiresSupervisor = true,
GrantRequiresSupervisor = false,
}
Config.AuditWebhook = '' -- Discord webhook for the audit log, empty = offExports
exports['XS-MDT']:IsAuthorized(src) -- can they open the MDT exports['XS-MDT']:GetOfficerInfo(src) -- name, citizenid, job, grade, department exports['XS-MDT']:AuditLog(action, src, details) exports['XS-MDT']:CreateDispatchCall(data) -- returns the call id exports['XS-MDT']:SendDispatchAlert(call) -- push a call to your dispatch exports['XS-MDT']:SuppressDispatch(callType, true) exports['XS-MDT']:RegisterCameras(list) -- add cameras, keyed by code exports['XS-MDT']:RegisterFootage(data) exports['XS-MDT']:CreateIncidentExternal(data) -- open a report exports['XS-MDT']:AttachEvidence(incidentId, evidence)
Troubleshooting
The MDT will not open
The job has to be listed under a department in Config.Departments, spelled exactly like your framework has it. With Config.OnDutyOnly on, the player also has to be on duty.
ps-dispatch shows as not running
The MDT finds it by folder name, renamed ones like ps-dispatch-main included. If yours is called something else, set Config.DispatchAlertResources. The Management panel shows which folder it matched.
Calls from my dispatch are not on the CAD
The Management panel should list your dispatch as calls in. If it says not running, set Config.DispatchAlertResources. A call only reaches a department the MDT knows, so its jobs have to be in Config.Departments. If XS-Dispatch runs your CAD, it keeps the call list instead.
Cars show Out when they are parked
The MDT reads parked cars from player_vehicles, including the in_garage column jg-advancedgarages uses. If you changed garage scripts and the old column is still there, set Config.VehicleStateFrom to 'state', 'in_garage' or 'stored'.
Database errors on start
The MDT updates its own tables on start and prints what it added. If something could not be added, the console names it. That is almost always the database user not being allowed to change tables.
Fines are not taken
Config.FineDeduction.Enabled has to be on, and the person has to be in the city. Citations are pay-later unless the officer ticks the box or AutoDeductCitations is on.
Jailing does nothing
Config.JailResource has to match your jail script, and the suspect has to be in the city when the arrest is logged. With it set to false, handle the XS-MDT:client:jailPlayer event yourself.
A camera is pointing at the sky
New cameras face north until they are aimed. Stand where the camera is, look at what it should see, and press Aim here on its card.
Old charges show next to the new charge book
The old default charges are removed on start once the new book is in. Ones you renamed or re-priced yourself are kept on purpose, so remove those in Penal Codes.
Updating
- Back up your database.
- Keep a copy of your
config.lua. - Replace the whole folder and start the server. The database updates on its own.
- Copy your config changes into the new
config.lua.
Coming from 2.x, the new config has a lot more in it, so copy your changes over rather than keeping the old file.
Support
Include your framework and its version, the job and grade you tested with, the full console error, and what you did right before it happened.