# 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.*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://atiysus-organization.gitbook.io/aty-scripts/introduction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
