> For the complete documentation index, see [llms.txt](https://atiysus-organization.gitbook.io/aty-scripts/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://atiysus-organization.gitbook.io/aty-scripts/mdt/introduction.md).

# Introduction

## aty\_mdt — Advanced Mobile Data Terminal

> A professional-grade, multi-department Mobile Data Terminal for FiveM servers running **ESX** or **QBCore**, authored by **atiysu**.

***

### Overview

`aty_mdt` is a comprehensive, full-featured Mobile Data Terminal built on a modern Vue.js NUI frontend. It is designed from the ground up to serve **Police**, **Sheriff**, and **EMS** departments with a rich set of tools for record-keeping, surveillance, evidence management, and real-time communication.

Key design goals:

* **Multi-Framework:** Native support for ESX (Legacy & Modern) and QBCore/Qbox.
* **Permission-Driven:** Every action is gated by a per-job, per-grade permission system configurable in `config.lua`.
* **Live Sync:** Data updates (dispatches, unit statuses, chat messages, new records) are pushed in real-time to all open MDTs using `GlobalState` and client events.
* **Dynamic Themes:** The tablet's color scheme, logo, and department title auto-adapt to the player's current job.
* **OneSync Compatible:** Bodycam and vehicle cam positions are broadcast server-side at 100ms intervals, ensuring compatibility with OneSync Infinity where entity streaming is limited.

***

### Features

#### 🏛️ Law Enforcement Suite

| Feature                | Description                                                                                                |
| ---------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Citizen Records**    | Full profile view with mugshot, DOB, licenses, vehicles, properties, warrants, notes, reports, and charges |
| **Vehicle Records**    | Lookup by plate, view owner details, registered notes, BOLOs, and vehicle history                          |
| **Reports**            | Create, update, and delete detailed incident reports with officer/suspect tagging                          |
| **Incidents**          | Track open/closed major incidents with citizens, priority, and tags                                        |
| **Warrants**           | Issue and close arrest warrants for specific citizens                                                      |
| **BOLOs**              | Issue and resolve "Be On the Lookout" notices for people and vehicles                                      |
| **Penal Code**         | Manage charge categories and individual charges with fine/sentence ranges                                  |
| **Judgements**         | Issue fines and prison sentences; automatically creates billing entries                                    |
| **Weapons Registry**   | Register and track weapon serial numbers and owners                                                        |
| **License Management** | Add or suspend any of the configured license types per citizen                                             |
| **GPS Codes**          | Store and share named GPS waypoints within the department                                                  |
| **Outfit Codes**       | Store and share saved outfit configurations by rank                                                        |
| **Staff Management**   | HR-level ability to fire staff members directly from the MDT                                               |
| **Internal Chat**      | Real-time, job-isolated messaging for on-duty officers                                                     |
| **Mugshot Camera**     | First-person mode integration to capture in-game citizen mugshots                                          |

#### 🏥 EMS Suite

| Feature                  | Description                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------- |
| **Treatment Logs**       | Full patient treatment history with body regions and tags                             |
| **Medications Database** | Manage a medication catalog with price and dosage information                         |
| **Medical Bills**        | Issue and view medical bills; integrates with billing scripts                         |
| **Blood Tests**          | Request a voluntary blood draw from an online citizen; result stored to their profile |
| **Death Records**        | Formal documentation of in-game deaths with cause and damage details                  |

#### 📹 Surveillance System

| Feature              | Description                                                                     |
| -------------------- | ------------------------------------------------------------------------------- |
| **Static CCTVs**     | View any configured fixed camera around the map                                 |
| **Live Bodycams**    | View the real-time position and heading of any officer wearing their bodycam    |
| **Vehicle Cameras**  | Attach a live feed to any vehicle the officer is in                             |
| **CCTV Creator**     | In-world prop placement tool to add new cameras to the database                 |
| **CCTV Editor**      | In-world editor to rename, delete, or move existing cameras                     |
| **Vision Modes**     | Cycle between Normal, Thermal, and Night Vision                                 |
| **Zoom**             | Mouse scroll wheel zooming for static cameras (10°–60° FOV)                     |
| **Entity Detection** | Raycast hover shows player name/DOB and vehicle make/plate while in camera view |

#### 🧬 Evidence System

| Feature                  | Description                                                                              |
| ------------------------ | ---------------------------------------------------------------------------------------- |
| **Shell Drops**          | Every player firing a weapon drops a shell at their location with a configurable timeout |
| **Flashlight Detection** | Officers holding a flashlight item can see nearby shells highlighted on screen           |
| **Shell Collection**     | Pick up shells (requires evidence bag item) for submission to the locker                 |
| **DNA Lab**              | Process shells at a dedicated lab location to link them to a weapon/suspect              |
| **Evidence Locker**      | Secure storage for collected evidence entries                                            |

***

### Dependencies

| Resource          | Purpose                                                   | Required       |
| ----------------- | --------------------------------------------------------- | -------------- |
| `aty_lib`         | Core multi-framework library (ESX/QBCore), callbacks, SQL | ✅ Yes          |
| `ox_lib`          | Blood test progress bar UI and alert dialogs              | ⚠️ Recommended |
| `aty_dispatchv2`  | Dispatch system integration for incoming calls            | ⚠️ Optional    |
| `aty_fingerprint` | Fingerprint merge into citizen profiles                   | ⚠️ Optional    |

***

### Installation Guide

#### Step 1 — Download & Place

Place the `aty_mdt` folder inside your resources directory, typically inside a category folder:

```
resources/
  [aty]/
    aty_lib/       ← must be present and started first
    aty_mdt/       ← this resource
```

#### Step 2 — Database Setup

> \[!CAUTION] The `drop_create.sql` file will **DROP and recreate** all MDT tables. Only run it on a fresh installation. For existing servers, import only the tables you are missing.

Execute the SQL files in the `data/` folder in this order:

| File              | When to Run        | Description                                                      |
| ----------------- | ------------------ | ---------------------------------------------------------------- |
| `drop_create.sql` | Fresh install only | Creates all MDT tables and seeds default charges and medications |
| `cctv.sql`        | Fresh install      | Pre-populates the `cctv` table with city-wide camera positions   |
| `esx.sql`         | If using ESX       | Framework-specific column additions for ESX user tables          |
| `qb.sql`          | If using QBCore    | Framework-specific column additions for QBCore player tables     |
| `seed.sql`        | Optional           | Additional seed data for charges and categories                  |

**For ESX servers**, the following columns are automatically added to your `users` table:

```sql
ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `blood_type` VARCHAR(10) DEFAULT NULL;
ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `mugshot` LONGTEXT DEFAULT NULL;
```

#### Step 3 — Add Items to Inventory

The MDT uses up to five items. Register them with your inventory of choice.

**For ox\_inventory** — Copy from `items/ox.lua` and paste into your `ox_inventory/data/items.lua`:

```lua
['mdt']          = { label = 'MDT',             weight = 1200, stack = true, close = true },
['bodycam']      = { label = 'Bodycam',          weight = 1200, stack = true, close = true },
['vehcam']       = { label = 'Vehicle Camera',   weight = 1200, stack = true, close = true },
['bullet_shell'] = { label = 'Bullet Shell',     weight = 1200, stack = true, close = true },
['evidence_bag'] = { label = 'Evidence Bag',     weight = 1200, stack = true, close = true },
```

**For QBCore** — Copy from `items/qb.lua` and paste into your `qb-core/shared/items.lua`. Item images (`mdt.png`, `bodycam.png`, etc.) should be placed in `qb-inventory/html/images/`.

**For ESX** — Add items via your database `items` table or through your ESX admin panel. Item names must match exactly.

#### Step 4 — Resource Startup

Add both resources to your `server.cfg`. **Order matters** — `aty_lib` must start first:

```cfg
ensure aty_lib
ensure aty_mdt
```

***

### Configuration Manual

All configuration is done in `shared/config.lua`. This file is shared between the client and server.

#### Core Settings

```lua
Config.UseQbox = false  -- Set to true if using Qbox (a modified QBCore)
Config.Phone = "none"   -- Phone integration: "none" or "lb" (lb-phone)
```

#### Access & Opening Methods

```lua
Config.Command = "mdt"               -- Slash command to open the MDT  (/mdt)
Config.Key = "F6"                    -- Default keybind (configurable per-player in GTA settings)
Config.ItemRequired = true           -- If true, player must have the item in their inventory
Config.ItemName = "mdt"             -- The exact item name required
Config.OpenWithItem = true           -- Allow opening via item use
Config.OpenWithCommand = true        -- Allow opening via the command/keybind
```

> \[!NOTE] If `ItemRequired` is `true`, the server validates the item server-side before the tablet opens, preventing exploits.

#### Billing Integration

The MDT's judgement system and medical bills can automatically send invoices via a supported billing script.

```lua
Config.BillingScript = "esx_billing"  -- Options: "none", "auto", "esx_billing", "okokBilling", "RxBilling"
Config.BillingAccount = "society_police"  -- The society account fines are paid into
```

| Value           | Behavior                                                                  |
| --------------- | ------------------------------------------------------------------------- |
| `"none"`        | Bills are recorded in the MDT database only; no in-game money is deducted |
| `"auto"`        | Automatically detects which billing script is running on the server       |
| `"esx_billing"` | Uses the standard ESX billing script                                      |
| `"okokBilling"` | Uses okokBilling or okokBillingV2                                         |
| `"RxBilling"`   | Uses the RxBilling resource                                               |

#### Tablet Themes & Branding

Each job can have its own custom theme. The theme is applied dynamically when the MDT is opened.

```lua
Config.TabletTypes = {
    ["police"] = {
        logo = "police_logo.png",       -- Filename in web/build/assets/
        title = "Police Department",    -- Department name shown in the UI header
        primaryColor = "#69FFEC",       -- Main accent/highlight color (hex)
        accentColor = "#69FFEC",        -- Secondary accent (usually same as primary)
        backgroundColor = "#0e1117",    -- Main background color
        secondaryColor = "#161922",     -- Card/panel background color
    },
    ["sheriff"] = { ... },
    ["ambulance"] = { ... },
}
```

> \[!TIP] Logo files must be placed inside `web/build/assets/`. The `title` field appears in the top-left of the tablet header.

**Adding a new department:**

1. Add a new entry to `Config.TabletTypes` with your job name as the key.
2. Add the job name as a key in `Config.Permissions` with the allowed actions.
3. Restart the resource.

#### Evidence System

```lua
Config.EvidenceSystem = {
    Enable = true,                              -- Toggle the entire evidence system on/off
    MenuCoords = vector3(0, 0, 0),             -- Location of the evidence submission menu (set your coords)
    LockerCoords = vector3(0, 0, 0),           -- Location of the evidence storage locker
    ShellItem = "bullet_shell",                -- Item name for bullet casings
    EmptyBagItem = "evidence_bag",             -- Item required to pick up shells from the ground
    FlashlightItems = {
        "weapon_flashlight",                    -- Weapon model name that acts as a flashlight
        "flashlight",                           -- Alternative item name
    },
    DnaTestDuration = 12,                      -- Duration of the DNA test animation (in seconds)
    DropTimeout = 10000,                       -- Minimum cooldown between shell drops (milliseconds)
    LabCoords = vector4(441.39, -993.46, 30.68, 251.28), -- Location of the DNA Lab (MRPD default)
    SaveInterval = 600000,                     -- How often (ms) shell data is saved to DB (10 minutes)
}
```

> \[!IMPORTANT] Set `MenuCoords` and `LockerCoords` to real in-world locations on your server. The default `vector3(0, 0, 0)` will spawn the interaction points at the world origin.

#### CCTV & Surveillance

```lua
Config.AllowCopsEditCCTV = true     -- If true, authorized grades can use the CCTV Creator/Editor
Config.JobRestrictedCCTV = true     -- If true, officers only see cameras tagged with their own job

Config.CCTVJobs = {                 -- Jobs that appear in the "Active Units" list and can use bodycams
    "police",
    "ambulance",
}

Config.BodycamItem = "bodycam"      -- Item name required to toggle bodycam (if itemRequired logic applies)
Config.VehcamItem = "vehcam"        -- Item name required to toggle vehicle cam
```

> \[!NOTE] When `JobRestrictedCCTV = true`, a `police` officer will NOT see CCTV cameras tagged with `job = "ambulance"` in the database, and vice versa. Cameras with a `NULL` or empty `job` column are visible to all departments.

#### Personal Lockers

Lockers are personal stash points per-job, using your inventory's stash system via `aty_lib`.

```lua
Config.Lockers = {
    ["police"] = {
        { coords = vector3(450.44, -992.33, 30.68), label = "Personal Locker" }, -- MRPD
        { coords = vector3(-1107.82, -825.21, 19.31), label = "Personal Locker" }, -- Vespucci
    },
    ["sheriff"] = {
        { coords = vector3(1851.52, 3690.62, 34.22), label = "Personal Locker" },
    },
    ["ambulance"] = {
        { coords = vector3(301.88, -597.51, 43.28), label = "Personal Locker" },
    }
}
```

Each entry creates an interaction zone near the specified coordinates. The stash holds up to **50 slots** and **100,000 weight units**.

#### Permissions System

This is the most powerful part of the configuration. Every action in the MDT is gated by this table.

```lua
Config.Permissions = {
    ["police"] = {
        -- Set to `true` to allow ALL grades in the job to perform this action.
        -- Set to a table of grade numbers (e.g., {2, 3, 4}) to restrict to those grades only.

        ["create_report"]             = true,      -- All police can create reports
        ["update_report"]             = {4},        -- Only grade 4 can edit reports
        ["delete_report"]             = {4},
        ["create_evidence"]           = true,
        ["delete_evidence"]           = {4},
        ["create_warrant"]            = true,
        ["close_warrant"]             = {1, 2, 3, 4},
        ["create_bolo"]               = true,
        ["update_bolo"]               = true,
        ["resolve_bolo"]              = true,
        ["create_incident"]           = true,
        ["update_incident"]           = true,
        ["delete_incident"]           = {4},
        ["create_weapon"]             = {2, 3, 4},
        ["delete_weapon"]             = {2, 3, 4},
        ["add_license"]               = {2, 3, 4},
        ["suspend_license"]           = {2, 3, 4},
        ["create_note"]               = true,
        ["delete_note"]               = {2, 3, 4},
        ["manage_cctv"]               = {4},        -- Only grade 4 can add/edit/delete CCTV cameras
        ["use_bodycam"]               = true,
        ["use_vehcam"]                = true,
        ["declare_deceased"]          = {2, 3, 4},
        ["create_judgement"]          = true,       -- All officers can issue fines/sentences
        ["create_charge"]             = {4},        -- Only grade 4 can add new penal code charges
        ["update_charge"]             = {4},
        ["delete_charge"]             = {4},
        ["create_charge_category"]    = {4},
        ["update_charge_category"]    = {4},
        ["delete_charge_category"]    = {4},
        ["manage_gps_codes"]          = {3, 4},
        ["manage_outfit_codes"]       = {3, 4},
        ["manage_staff"]              = {4},        -- Only grade 4 can fire staff from MDT
    },
    ["ambulance"] = {
        ["create_treatment"]  = true,
        ["delete_treatment"]  = {3},
        ["create_medication"] = {3},
        ["delete_medication"] = {3},
        ["create_bill"]       = true,
        ["create_note"]       = true,
        ["delete_note"]       = {2, 3},
        ["manage_gps_codes"]  = {2, 3},
        ["manage_outfit_codes"] = {2, 3},
        ["manage_staff"]      = {3},
        ["declare_deceased"]  = {2, 3},
        ["create_evidence"]   = true,
        ["delete_evidence"]   = {3},
        ["create_report"]     = true,
        ["update_report"]     = {3},
        ["delete_report"]     = {3},
    },
}
```

> \[!IMPORTANT] Jobs NOT listed in `Config.TabletTypes` cannot open the MDT at all, regardless of permissions. `Config.Permissions` only controls what actions are available to jobs that CAN open the tablet.

**Firing (HR action):**

```lua
Config.FireJob = "unemployed"  -- The job name assigned when firing a staff member
Config.FireGrade = 0           -- The grade assigned after firing
```

#### Licenses Configuration

These are the in-game licenses that can be added/suspended per-citizen.

```lua
Config.Licenses = {
    { id = 1, type = "drivers_license", label = "driver_license"  },
    { id = 2, type = "pilot_license",   label = "pilot_license"   },
    { id = 3, type = "weapon_license",  label = "weapon_license"  },
    { id = 4, type = "boat_license",    label = "boating_license" },
}
```

The `type` field is the value stored in the database; `label` is what is displayed in the UI locale system.

#### Database Table Names

If your server uses non-standard table names (e.g., QBCore uses `players` instead of `users`), update these:

```lua
Config.Tables = {
    Users    = "users",           -- Main player table (ESX: "users", QBCore: "players")
    Vehicles = "owned_vehicles",  -- Vehicle table (ESX: "owned_vehicles", QBCore: "player_vehicles")
    -- All other tables are MDT-specific and do not need to be changed normally
    Note = "mdt_user_notes", Warrant = "mdt_user_warrants", Report = "mdt_reports",
    Evidence = "mdt_evidences", Treatment = "mdt_treatments", Medication = "mdt_medications",
    Bill = "mdt_bills", ChatMessage = "mdt_chat_messages", CCTV = "cctv",
    Incident = "mdt_incidents", Charge = "mdt_charges", ChargeCategory = "mdt_charge_categories",
    Bolo = "mdt_bolos", Weapon = "mdt_weapons", Death = "mdt_deaths",
    Judgement = "mdt_user_judgement", UserReports = "mdt_user_reports",
    VehicleBolos = "mdt_vehicle_bolos", GpsCode = "mdt_gps_codes",
    OutfitCode = "mdt_outfit_codes", BloodTest = "mdt_user_bloodtests",
}
```

#### BOLO Settings

Controls the date picker range when setting a BOLO's expiration date.

```lua
Config.BoloSettings = {
    MinDays = 1,   -- Minimum days from today the expiration date can be set
    MaxDays = 30,  -- Maximum days from today the expiration date can be set
}
```

#### Date Formats

Controls how dates are displayed throughout the MDT UI.

```lua
Config.DateFormat     = "%d/%m/%Y"           -- Used for dates (e.g., 05/04/2026)
Config.DateTimeFormat = "%d/%m/%Y %H:%M"     -- Used for timestamps (e.g., 05/04/2026 14:30)
```

These use standard Lua `os.date` format strings.

#### Fingerprint Integration

If you run `aty_fingerprint` alongside the MDT, citizen profiles can be automatically enriched with fingerprint data.

```lua
Config.FingerprintMerge = {
    Enabled  = false,              -- Set to true to enable
    Resource = "aty_fingerprint",  -- The resource name of your fingerprint script
}
```

When enabled, the MDT citizen list is merged with fingerprint records. Only DB fields that exist in both systems are overwritten. Matching is done by `identifier` (ESX) or `citizenid` (QBCore/Qbox).

#### Phone Integration

```lua
Config.Phone = "lb"  -- Set to "lb" to enable lb-phone integration for phone number lookups
```

#### Other Settings

```lua
Config.EnableTransparencyOnLeave = true  -- Tablet becomes semi-transparent when mouse is outside the NUI area,
                                          -- allowing the player to move while the MDT is open
Config.AutoRegisterWeapons = true        -- Automatically pulls weapons from player inventories and
                                          -- displays them in the Weapons Registry

Config.BloodTypes = {"A+","A-","B+","B-","AB+","AB-","O+","O-"}  -- Valid blood type options
Config.BloodTestDuration = 10000         -- How long the blood draw animation takes (ms)

Config.DefaultProfileImage = "https://..."  -- URL for citizens who have not had a mugshot taken
Config.ImageWebhook = "https://discord.com/api/webhooks/..."  -- Discord webhook for uploading mugshot photos

Config.ReportLocations = {               -- World positions where officers can create a report by pressing E
    vector3(441.21, -981.85, 30.69),    -- MRPD
    vector3(-1098.37, -830.7, 19.3),    -- Vespucci
}
```

***

### Localization

The MDT supports multiple languages. The language is set at the top of `shared/locale.lua`:

```lua
Locale = "en"  -- Change this to switch language
```

**Supported languages out of the box:**

| Code | Language   |
| ---- | ---------- |
| `en` | English    |
| `tr` | Turkish    |
| `fr` | French     |
| `es` | Spanish    |
| `de` | German     |
| `pt` | Portuguese |

**Adding a new language:**

1. Open `shared/locale.lua`
2. Find the `["en"] = { ... }` block and copy it entirely
3. Paste it below, rename the key to your language code (e.g., `["it"]`)
4. Translate all string values inside the block
5. Change `Locale = "it"` at the top of the file

***

### Usage Guide — Law Enforcement

#### Opening the MDT

There are three ways to open the MDT:

| Method                 | Requirement                                  | Notes                            |
| ---------------------- | -------------------------------------------- | -------------------------------- |
| Keybind (default `F6`) | Player must be a valid job                   | Rebindable in GTA V key settings |
| Command (`/mdt`)       | `OpenWithCommand = true` in config           | Can be disabled                  |
| Item Use               | `ItemRequired = true` and possess `mdt` item | Item is validated server-side    |

When the MDT opens, the player's character plays a tablet-holding animation and a physical tablet prop (`prop_cs_tablet`) is attached to the left hand.

#### Dashboard & Active Units

The Dashboard is the first screen shown. It displays:

* **Your profile card:** Name, rank, department, and current status
* **Active Units:** A real-time list of all online officers that belong to `Config.CCTVJobs`, showing their name, rank, callsign, and current street location (updated every 10 seconds)
* **Recent Dispatches:** The last 50 incoming dispatch alerts from `aty_dispatchv2`

**Changing Your Status:** Click your status button on your profile card to cycle through:

* Available
* Busy
* Off Duty
* On Scene

Your status is broadcast server-side via `GlobalState` and appears on all other officers' dashboards in real-time.

**Responding to a Dispatch:** Click **Approve** on any incoming dispatch to set your GPS waypoint to the incident location. Click **Decline** to dismiss it from your view.

#### Citizen Records

Navigate to the **Citizens** tab to search and browse all players registered in your server's database.

**Search:** Type any part of a citizen's name or identifier to filter the list instantly.

**Citizen Profile View:** Click on a citizen to expand their full profile, which includes:

| Section           | Contents                                                     |
| ----------------- | ------------------------------------------------------------ |
| **Personal Info** | Mugshot, full name, DOB, sex, phone number, blood type       |
| **Licenses**      | Active/suspended status for all configured license types     |
| **Vehicles**      | All registered vehicles with model, make, and plate          |
| **Properties**    | Any registered properties                                    |
| **Notes**         | Internal department notes (visible/restricted by job)        |
| **Reports**       | All reports the citizen is linked to                         |
| **Warrants**      | Active and closed warrants                                   |
| **Judgements**    | All issued fines and prison sentences across all departments |
| **Blood Tests**   | Results from any blood tests conducted by EMS                |

**Taking a Mugshot:** From the citizen profile, click the **Take Mugshot** button. The MDT will close and put you into a first-person camera mode. Press `Left Click` or `D-Pad Down` (controller) to capture the screenshot. The image is uploaded to your configured Discord webhook and saved to the citizen's profile.

#### Vehicle Records

Navigate to the **Vehicles** tab to search all registered vehicles.

**Search by Plate:** Type a plate number to instantly filter.

**Vehicle Profile View:**

* Owner name and identifier
* Vehicle make and model (resolved from GTA V game hashes)
* Registration status
* Attached notes and BOLOs

#### Reports

| Action | Required Permission | Description                                                                                          |
| ------ | ------------------- | ---------------------------------------------------------------------------------------------------- |
| Create | `create_report`     | Opens a form to document an incident with officers, suspects, vehicles, weapons, and evidence fields |
| Update | `update_report`     | Edit an existing report's content                                                                    |
| Delete | `delete_report`     | Permanently remove a report                                                                          |

Reports support a **risk level** (Low / Medium / High / Critical) with color coding.

**Reports can also be created in-world** by walking up to one of the `Config.ReportLocations` coordinates and pressing `E`.

#### Incidents

Incidents are for tracking **ongoing major events** (e.g., a bank robbery in progress).

| Field       | Description                    |
| ----------- | ------------------------------ |
| Name        | The incident title             |
| Priority    | Low / Medium / High / Critical |
| Status      | Open / Closed                  |
| Citizens    | Tag citizens involved          |
| Tags        | Freeform tags                  |
| Description | Detailed narrative             |

#### Warrants

| Action         | Permission       | Notes                                                           |
| -------------- | ---------------- | --------------------------------------------------------------- |
| Create Warrant | `create_warrant` | Must specify citizen, reason, and expiration date               |
| Close Warrant  | `close_warrant`  | Requires selecting a closure type (e.g., "Served", "Dismissed") |

Active warrants appear prominently on a citizen's profile.

#### BOLOs (Be On the Lookout)

BOLOs are broadcast alerts for suspects or vehicles that officers should watch for.

**BOLO Fields:**

* Title and description
* Suspect tags (dangerous, armed)
* Optional vehicle information
* Priority (Low / Medium / High / Critical)
* Expiration date (within the `MinDays`–`MaxDays` configured range)

#### Charges & Penal Code

The **Charges** tab lists all configured penal code entries. These are used when creating a Judgement against a citizen.

| Action                 | Permission               |
| ---------------------- | ------------------------ |
| Create charge category | `create_charge_category` |
| Create charge          | `create_charge`          |
| Update charge          | `update_charge`          |
| Delete charge          | `delete_charge`          |

Each charge has: category, title, type (Infraction/Misdemeanor/Felony), minimum/maximum fine, minimum/maximum sentence, and priority.

#### Weapons Registry

Weapons can be registered manually or automatically:

* **Manual:** From the Weapons tab (requires `create_weapon` permission)
* **Automatic:** Set `Config.AutoRegisterWeapons = true` to pull all weapons from player inventories
* **Via Export:** Other scripts can register weapons using `exports['aty_mdt']:RegisterWeapon(data)`

Each weapon has: serial number, type, label (friendly name), owner name, date registered, and created-by officer.

#### Evidence System Usage

**Shell Collection Workflow:**

1. An officer fires their weapon → a shell is automatically dropped at their position (subject to `DropTimeout` cooldown).
2. Any officer holding a **flashlight item** (see `Config.EvidenceSystem.FlashlightItems`) will see nearby shells highlighted with yellow markers.
3. Walk within 1.5m of a shell and press `E` (while holding the flashlight) to pick it up. You must have an `evidence_bag` in your inventory.
4. The collected shell appears in the MDT Evidence tab.

**DNA Testing:** Go to the `Config.EvidenceSystem.LabCoords` location. Open the MDT Evidence tab and click **Run DNA Test** on a collected shell. After a short animation, the shell is linked to the weapon used to fire it (and by extension, to the weapon's registered owner).

**Evidence Locker:** Evidence can be submitted to a secure locker at `Config.EvidenceSystem.LockerCoords`. This changes the evidence status from `collected` to `deposited`.

#### GPS & Outfit Codes

Officers with `manage_gps_codes` or `manage_outfit_codes` permission can access these sections.

* **GPS Codes:** Named waypoints that any officer in the department can quickly set as their GPS destination (e.g., `MRPD Front`, `Hospital`).
* **Outfit Codes:** Stored outfit configurations indexed by rank/grade. Officers can apply a saved outfit instantly.

#### Internal Chat

The **Chat** tab provides a department-isolated messaging channel. Messages are stored in the database and loaded on MDT open. New messages are pushed in real-time to all online officers of the same job.

The chat is visible to all grades of the department but cannot be seen by officers of other departments.

#### Staff Management

Officers with the `manage_staff` permission (typically grade 4) can access the **Staff** tab to view all online/offline staff and fire them. Firing a staff member assigns them `Config.FireJob` and `Config.FireGrade`.

***

### Usage Guide — EMS / Ambulance

EMS players see a different set of tabs when they open their MDT (with a red ambulance theme).

#### Patient Management (Treatments)

| Action               | Permission         |
| -------------------- | ------------------ |
| Create treatment log | `create_treatment` |
| Delete treatment log | `delete_treatment` |

Treatment logs include: patient name, citizen identifier, treatment title, description, body regions affected, and tags.

#### Medications Database

| Action            | Permission          |
| ----------------- | ------------------- |
| Add medication    | `create_medication` |
| Delete medication | `delete_medication` |

Medications have: name, description, price, and optional dosage range (min/max). These are used as a reference catalog for available medications.

#### Medical Bills

| Action      | Permission    |
| ----------- | ------------- |
| Create bill | `create_bill` |

Bills can be sent to an online citizen. If `Config.BillingScript` is set (not `"none"`), the money is deducted from the citizen's in-game bank account. Bills also appear in the citizen's **Judgements** tab for the police to see (labeled `[EMS]`).

#### Blood Tests

The blood test system allows EMS to formally document a citizen's blood type.

**Workflow:**

1. EMS officer opens a citizen profile in the MDT.
2. Clicks **Request Blood Test**.
3. If the citizen is **online**, they receive an `ox_lib` alert dialog asking them to consent.
4. If they accept, a progress bar animation plays (`ox_lib` required).
5. Once complete, the citizen's blood type is determined (from DB or randomly assigned) and saved to the `mdt_user_bloodtests` table.
6. The result appears on the citizen's profile in the MDT.

> \[!NOTE] Blood tests require `ox_lib` to be installed and started. Without it, only a basic notification is shown and the interactive flow does not work.

#### Death Records

| Action           | Permission         |
| ---------------- | ------------------ |
| Declare deceased | `declare_deceased` |

Death records include: citizen identifier, name, reason, damage description, status (e.g., DOA), tags, date of death, and the declaring officer.

***

### Surveillance System

#### Static CCTV Cameras

The MDT Surveillance tab displays all cameras from the `cctv` database table. Clicking **View** on a camera closes the tablet and activates the camera feed.

CCTV camera props are dynamically spawned in-world when a player gets within **50 meters** of the camera's coordinates. They are automatically cleaned up when the player moves beyond that distance.

#### Live Bodycams

When an officer activates their bodycam item (`bodycam`), they register a "dynamic camera" on the server under `type = "bodycam"`. This feed appears in the MDT Surveillance list and can be viewed by any authorized officer.

**Server-Side Position Broadcast:** Due to OneSync Infinity limitations (entities outside a viewer's streaming range cannot be found by network ID), the bodycam position is broadcast **server-side** every 100ms directly from the owner's ped coordinates. This ensures the camera feed follows the officer accurately regardless of distance.

#### Vehicle Cameras (Vehcam)

Similar to bodycam but attached to a vehicle. The officer must be **inside a vehicle** to activate it. The vehicle's plate and model are used as the camera label.

Only one vehicle camera can be active per player at a time.

#### CCTV Creator Mode

Officers with the `manage_cctv` permission can add new cameras to the world.

**How to Start:** From the MDT Surveillance tab → **Add Camera** button (or via a server event trigger `aty_mdt:startCCTVCreator`).

**Creator Controls:**

| Key                  | Action                                               |
| -------------------- | ---------------------------------------------------- |
| Move mouse           | The camera prop follows your gameplay camera raycast |
| `←` / `→` Arrow Keys | Rotate camera heading                                |
| `↑` Arrow Key        | Cycle to next camera prop model                      |
| `↓` Arrow Key        | Cycle to previous camera prop model                  |
| `G`                  | Toggle pole attachment on/off                        |
| `Enter`              | Confirm placement (opens name input)                 |
| `Backspace`          | Cancel and exit creator                              |

**Prop Models Available:**

* `prop_cctv_cam_06a`
* `prop_cctv_cam_02a`
* `m24_1_prop_m24_1_carrier_bank_cctv_02`
* `prop_cctv_cam_05a`
* `p_cctv_s`
* `prop_cctv_cam_04c`
* `hei_prop_bank_cctv_01`
* `xm_prop_x17_server_farm_cctv_01`

After pressing `Enter`, a text input appears. Type the camera name and submit to save it to the database.

#### CCTV Editor Mode

Hover your gameplay camera over any existing CCTV prop to select it (it will highlight in red). When selected:

| Key                 | Action                                                                  |
| ------------------- | ----------------------------------------------------------------------- |
| `DEL`               | Delete the camera from the database                                     |
| `R`                 | Rename the camera (opens name input)                                    |
| `E`                 | Delete and re-enter Creator mode with the same prop model to reposition |
| `Backspace` / `ESC` | Exit editor mode                                                        |

#### Camera View Controls

When viewing any camera (static or dynamic):

| Key                 | Action                                              |
| ------------------- | --------------------------------------------------- |
| `W` / `S`           | Tilt camera up / down                               |
| `A` / `D`           | Pan camera left / right                             |
| `Mouse Scroll Up`   | Zoom in (static cams only, min FOV: 10°)            |
| `Mouse Scroll Down` | Zoom out (static cams only, max FOV: 60°)           |
| `E`                 | Cycle vision mode (Normal → Thermal → Night Vision) |
| `Backspace`         | Exit camera view and return to normal               |

**Vision Modes** are disabled for dynamic (bodycam/vehcam) feeds; they only work on static CCTV cameras.

**Entity Detection:** While in camera view, a raycast runs continuously from the camera's crosshair. If it hits a player, their name, DOB, and sex are shown in a HUD overlay. If it hits a vehicle, the make, plate, and class are displayed.

***

### Dispatch Integration

`aty_mdt` listens for the `aty_dispatchv2:client:sendDispatch` event from `aty_dispatchv2`. When a dispatch is received:

1. It's added to an in-memory list (max 50 entries)
2. It's pushed to the MDT NUI in real-time
3. Officers can Approve (sets GPS waypoint) or Decline (dismisses) the call

**Expected Dispatch Data Format:**

```lua
TriggerClientEvent("aty_dispatchv2:client:sendDispatch", -1, {
    title = "Armed Robbery",
    code = "10-30",
    message = "Armed robbery in progress",
    jobs = {"police", "sheriff"},            -- Which jobs receive this dispatch
    coords = vector3(441.0, -981.0, 30.0),  -- Map blip location
    image = "https://...",                   -- Optional image URL
    information = {
        description = "Suspect armed with pistol",
        caller = "John Doe",
        gender = "Male",
        street = "Elgin Avenue, Downtown LS",
        phone = "555-0100",
    }
})
```

***

### Personal Lockers Usage

Once configured in `Config.Lockers`, a persistent stash interaction appears near each locker coordinate.

* Walk within interaction range and press `E` to open your personal locker.
* The stash is linked to your player identifier, so only you can access it.
* Capacity: **50 slots** / **100,000 weight units**.
* Lockers use your server's inventory system via `aty_lib`'s `RegisterStash` module.

***

### Developer Exports

#### Server-Side: `RegisterWeapon`

Register a weapon serial number into the MDT database from any other resource. Triggers a live update for all open MDT tablets.

```lua
---@param data table
---@return boolean success
local success = exports['aty_mdt']:RegisterWeapon({
    serialNumber = "SN-123456",   -- required: unique serial number string
    owner        = "John Doe",    -- required: the owner's display name
    type         = "WEAPON_PISTOL", -- optional: weapon type/model string
    label        = "Combat Pistol"  -- optional: friendly weapon label
})

if success then
    print("Weapon registered in MDT successfully.")
end
```

**Use Cases:**

* A gun shop script can call this when a weapon is sold.
* A crafting script can register crafted weapons.
* An admin script can bulk-register weapons.

***

### Database Schema Reference

The following tables are created by `data/drop_create.sql`:

| Table                   | Purpose                                                  |
| ----------------------- | -------------------------------------------------------- |
| `mdt_user_licenses`     | Citizen license records (type, status)                   |
| `mdt_user_judgement`    | Police fines and prison sentences                        |
| `mdt_user_notes`        | Internal department notes per citizen                    |
| `mdt_user_warrants`     | Active and closed arrest warrants                        |
| `mdt_user_reports`      | Reports that a citizen appears in                        |
| `mdt_user_bloodtests`   | Blood test results (auto-created on first run)           |
| `mdt_reports`           | Full report documents with all linked data               |
| `mdt_incidents`         | Major incident tracking                                  |
| `mdt_evidences`         | Physical evidence entries                                |
| `mdt_evidences_shells`  | Bullet casings collected from crime scenes               |
| `mdt_bolos`             | "Be On the Lookout" notices                              |
| `mdt_vehicle_bolos`     | Vehicle-specific BOLOs                                   |
| `mdt_vehicle_photos`    | Photos attached to vehicle records                       |
| `mdt_vehicle_notes`     | Notes attached to vehicle records                        |
| `mdt_weapons`           | Weapon serial number registry                            |
| `mdt_charges`           | Penal code charges                                       |
| `mdt_charge_categories` | Categories for penal code charges                        |
| `mdt_treatments`        | EMS treatment logs                                       |
| `mdt_medications`       | EMS medication catalog                                   |
| `mdt_bills`             | Medical bills issued by EMS or fines recorded internally |
| `mdt_deaths`            | Death records declared by EMS or police                  |
| `mdt_chat_messages`     | Persistent department chat history                       |
| `cctv`                  | Static CCTV camera locations                             |
| `mdt_gps_codes`         | Named department GPS waypoints                           |
| `mdt_outfit_codes`      | Saved outfit configurations by grade                     |

**Columns added to your framework's player table:**

```sql
-- For ESX (users table):
ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `blood_type` VARCHAR(10) DEFAULT NULL;
ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `mugshot` LONGTEXT DEFAULT NULL;
```

***

### Troubleshooting

#### MDT won't open

* Ensure `aty_lib` is started **before** `aty_mdt` in `server.cfg`.
* Ensure your player's job name exists as a key in `Config.TabletTypes`.
* If `ItemRequired = true`, confirm the player has the `mdt` item in their inventory.
* Check the server console for errors from `aty_lib` or `aty_mdt`.

#### "You do not have permission" when trying an action

* Check `Config.Permissions` for the player's job.
* Verify the player's grade matches the required grade list for that action.
* Grades in QBCore start at `0`, in ESX they may start at `0` or `1` depending on your setup.

#### Blank citizen profiles / "Could not retrieve MDT data"

* Verify `Config.Tables.Users` matches your database's player table name.
* Ensure your database user has `SELECT` privileges on that table.
* Check that `aty_lib`'s SQL module is correctly initialized.

#### "Camera not found" error

* Ensure you imported `data/cctv.sql` into your database.
* Try refreshing cameras from the CCTV tab in the MDT.
* Wait a few seconds after opening — the server throttles CCTV loads to every 5 seconds.

#### Bodycam / Vehcam feed is frozen or not moving

* This is normal if the officer is not moving — the server only updates every 100ms.
* Ensure the officer is **online** and within rendering range is **not required** due to the server-side broadcast system.
* If `aty_mdt:forceCloseDynamicCam` was triggered, the officer's camera was deactivated (they disconnected or died).

#### Blood test doesn't work / no prompt appears

* Install and ensure `ox_lib` is **started** on your server.
* Without `ox_lib`, only a notification is shown; no interactive flow is triggered.

#### MDT UI is blank after modifying the web source

* Run `npm install` followed by `npm run build` inside the `web/` directory.
* Ensure the built files exist in `web/build/`.

#### Dispatch calls not appearing in MDT

* Ensure `aty_dispatchv2` is started and sending the `aty_dispatchv2:client:sendDispatch` client event.
* Check that the dispatch's `jobs` array includes the officer's job name.
* The MDT only receives dispatches if the tablet is already open at the time the event fires; previously received dispatches are stored in memory for the session.

***

### FAQ

**Q: Can I add more than 3 jobs (police, sheriff, ambulance)?**\
A: Yes. Add a new entry to `Config.TabletTypes` and `Config.Permissions` with your custom job name. Any job listed in `TabletTypes` can open the MDT.

**Q: Can civilians open the MDT?**\
A: No. Only jobs listed in `Config.TabletTypes` can open the tablet, regardless of item possession.

**Q: Do I need to restart the resource after editing `config.lua`?**\
A: Yes. The config is loaded at startup on both client and server. A full `restart aty_mdt` is required for config changes to take effect.

**Q: How do I change the CCTV camera range for prop spawning?**\
A: The prop spawning range (50m) and the locker interaction range are hardcoded in `client/system/cctv.lua`. You can modify the `dist < 50.0` check to your desired value.

**Q: Does this work with ox\_inventory?**\
A: Yes. `aty_lib` abstracts the inventory interaction, supporting ox\_inventory, qb-inventory, ESX's default inventory, and others.

**Q: Can two officers view the same bodycam simultaneously?**\
A: Yes. The server-side observer system (`DynamicCamObservers`) supports multiple viewers per dynamic camera. Each viewer receives position broadcasts independently.

**Q: Is there a way to programmatically open the MDT for a player?**\
A: Yes. Trigger the following event from the server:

```lua
TriggerClientEvent("aty_mdt:client:open", playerSource)
```

This runs all the same item and job checks as the keybind/command.

***

*Developed by **atiysu** — part of the `[aty]` resource collection.*
