# FINGERPRINT SCANNER

### 1) Item Setup

Before configuring the script, you need to add the item to your inventory system. Choose the section that matches your server setup.

#### ESX (items.lua / database)

**Method 1 — items.lua** (if your server uses a file-based item list):

```lua
['fingerprint_scanner'] = {
    label = 'Fingerprint Scanner',
    weight = 500,
    stack = false,
    close = true,
    description = 'A portable fingerprint scanning device used by law enforcement.'
},
```

**Method 2 — SQL (direct database insert):**

```sql
INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) 
VALUES ('fingerprint_scanner', 'Fingerprint Scanner', 1, 0, 1);
```

**Method 3 — ESX Legacy (es\_extended/config/items.lua):**

```lua
{name = 'fingerprint_scanner', label = 'Fingerprint Scanner', weight = 1, stack = false, degrade = 0},
```

***

#### QBCore (qb-core/shared/items.lua)

```lua
['fingerprint_scanner'] = {
    name = 'fingerprint_scanner',
    label = 'Fingerprint Scanner',
    weight = 500,
    type = 'item',
    image = 'fingerprint_scanner.png',
    unique = true,
    useable = true,
    shouldClose = true,
    combinable = nil,
    description = 'A portable fingerprint scanning device used by law enforcement.'
},
```

***

#### QBox (qbx\_core/data/items.lua)

```lua
fingerprint_scanner = {
    label = 'Fingerprint Scanner',
    weight = 500,
    stack = false,
    close = true,
    description = 'A portable fingerprint scanning device used by law enforcement.',
},
```

***

#### ox\_inventory (ox\_inventory/data/items.lua)

```lua
fingerprint_scanner = {
    label = 'Fingerprint Scanner',
    weight = 500,
    stack = false,
    close = true,
    description = 'A portable fingerprint scanning device used by law enforcement.',
},
```

***

#### Giving Item In-Game (for testing)

**ESX:**

```
/giveitem [playerid] fingerprint_scanner 1
```

**QBCore:**

```
/giveitem [playerid] fingerprint_scanner 1
```

**ox\_inventory:**

```
/giveitem [playerid] fingerprint_scanner 1
```

> **⚠️ IMPORTANT:** Make sure your item image is placed in the correct inventory folder. For QBCore: `qb-inventory/html/images/`. For ox\_inventory: `ox_inventory/web/images/`. Name the file `fingerprint_scanner.png`.

***

### 2) Overview

Risk Fingerprint Scanner is a law enforcement tool allowing officers to scan civilian fingerprints and retrieve identity information featuring:

* 🔍 Real-time fingerprint scanning system
* 👮 Job-restricted access (configurable per department)
* 📋 Scan history per officer (stored in database)
* 🎭 Animated scanning interaction
* 🖥️ Modern MDT-style UI
* 📡 Item or command-based activation
* 🌐 Full localization support
* ⚙️ ESX, QBCore, and QBox compatibility

***

### 3) Dependencies

The following resources **must** be installed and started before `risk-fingerprint`:

```
ensure oxmysql
ensure risk-fingerprint
```

**Required:**

* [oxmysql](https://github.com/overextended/oxmysql) — Database wrapper

**Optional:**

* `risk-notify` — Only if using custom notifications

***

### 4) Basic Configuration

#### Command Activation

```lua
Config.UseCommand = true
Config.Command = 'fingerprint'
```

* **UseCommand:** `true` = players can open scanner via command
* **Command:** The command name (used as `/fingerprint`)

**Disable command (item-only):**

```lua
Config.UseCommand = false
```

**Custom command name:**

```lua
Config.Command = 'scanner'    -- /scanner
Config.Command = 'biometrics' -- /biometrics
Config.Command = 'fp'         -- /fp
```

#### Item Activation

```lua
Config.UseItem = true
Config.ItemName = 'fingerprint_scanner'
```

* **UseItem:** `true` = scanner opens when using item from inventory
* **ItemName:** Must match exactly the item name added in Section 1

**Disable item (command-only):**

```lua
Config.UseItem = false
```

**Both command AND item active simultaneously:**

```lua
Config.UseCommand = true
Config.UseItem = true
```

***

### 5) Scanning Settings

#### Scan Hold Duration

```lua
Config.ScanHoldDuration = 10000
```

**Time in milliseconds the target must hold the scanner button**

**Quick scan:**

```lua
Config.ScanHoldDuration = 3000   -- 3 seconds
```

**Standard scan:**

```lua
Config.ScanHoldDuration = 10000  -- 10 seconds (default)
```

**Long scan:**

```lua
Config.ScanHoldDuration = 20000  -- 20 seconds
```

#### Maximum Distance

```lua
Config.MaxDistance = 3.0
```

**Maximum distance in meters between officer and target**

**Close proximity (realistic):**

```lua
Config.MaxDistance = 2.0  -- Must be very close
```

**Standard:**

```lua
Config.MaxDistance = 3.0  -- Default
```

**Relaxed:**

```lua
Config.MaxDistance = 5.0  -- More forgiving
```

***

### 6) Animation Settings

```lua
Config.UseAnimation = true
Config.AnimDict = 'amb@world_human_security_shine_torch@male@base'
Config.AnimName = 'base'
Config.AnimFlag = 49
```

**How it works:**

* When officer initiates scan, animation plays automatically
* Animation stops when scan completes or is cancelled
* Set `UseAnimation = false` to disable

**Disable animation:**

```lua
Config.UseAnimation = false
```

**Alternative animations:**

```lua
-- Phone holding animation
Config.AnimDict = 'cellphone@call'
Config.AnimName = 'cellphone_call_listen_base'
Config.AnimFlag = 49

-- Tablet animation
Config.AnimDict = 'amb@world_human_seat_wall_tablet@female@base'
Config.AnimName = 'base'
Config.AnimFlag = 49

-- Searching animation
Config.AnimDict = 'amb@world_human_security_shine_torch@male@base'
Config.AnimName = 'base'
Config.AnimFlag = 49
```

**Animation flags:**

* `49` — Loop and hold animation (recommended)
* `1` — Loop
* `0` — Play once

***

### 7) Allowed Jobs

```lua
Config.AllowedJobs = {
    ['police'] = true,
    ['sheriff'] = true,
    ['lspd'] = true,
    ['bcso'] = true,
    ['sasp'] = true,
    ['ranger'] = true,
}
```

**Only players with these jobs can use the fingerprint scanner**

**How to find your job names:**

* ESX: Check your `jobs` table in the database
* QBCore: Check `qb-core/shared/jobs.lua`

**Adding/removing jobs:**

**Police only:**

```lua
Config.AllowedJobs = {
    ['police'] = true,
}
```

**Multiple law enforcement:**

```lua
Config.AllowedJobs = {
    ['police'] = true,
    ['sheriff'] = true,
    ['highway_patrol'] = true,
    ['detective'] = true,
    ['swat'] = true,
}
```

**Include federal agencies:**

```lua
Config.AllowedJobs = {
    ['police'] = true,
    ['sheriff'] = true,
    ['lspd'] = true,
    ['bcso'] = true,
    ['sasp'] = true,
    ['ranger'] = true,
    ['fib'] = true,
    ['iaa'] = true,
    ['usms'] = true,
}
```

> **⚠️ NOTE:** Job names must match **exactly** as they appear in your framework (case-sensitive). Test with `/fingerprint` in-game to verify.

***

### 8) UI Customization

#### Department Display

```lua
Config.DepartmentName = 'LSPD'
Config.DepartmentSub = 'Mobile Data Terminal'
```

* **DepartmentName:** Main title shown in the MDT UI header
* **DepartmentSub:** Subtitle shown under the main title

**Examples per department:**

**Los Santos Police:**

```lua
Config.DepartmentName = 'LSPD'
Config.DepartmentSub = 'Mobile Data Terminal'
```

**Blaine County:**

```lua
Config.DepartmentName = 'BCSO'
Config.DepartmentSub = 'Biometric Identification System'
```

**State Police:**

```lua
Config.DepartmentName = 'SASP'
Config.DepartmentSub = 'Fingerprint Analysis Unit'
```

**Generic:**

```lua
Config.DepartmentName = 'LAW ENFORCEMENT'
Config.DepartmentSub = 'Identity Verification System'
```

***

### 9) Localization / Strings

All UI and notification text can be customized:

```lua
Config.Locale = {
    -- Notifications (shown as in-game messages)
    ['no_permission'] = 'You do not have permission to use this.',
    ['no_player_nearby'] = 'No player nearby.',
    ['scanning'] = 'Scanning fingerprint...',
    ['scan_complete'] = 'Fingerprint scan complete.',
    ['scan_cancelled'] = 'Scan cancelled.',
    ['too_far'] = 'Target is too far away.',
    ['target_too_far'] = 'Target moved too far away.',
    ['target_cancelled'] = 'Target cancelled the scan.',
    ['you_too_far'] = 'You moved too far away.',
    
    -- UI Text (shown inside the MDT interface)
    ['ui_authenticate'] = 'Authenticate',
    ['ui_scanning_biometrics'] = 'Scanning biometrics...',
    ['ui_access_granted'] = 'Access Granted',
    ['ui_access_denied'] = 'Access Denied',
    ['ui_mdt_system'] = 'MDT SYSTEM',
    ['ui_officer'] = 'Officer',
    ['ui_scan_history'] = 'SCAN HISTORY',
    ['ui_no_records'] = 'No records found',
    ['ui_scan_fingerprint'] = 'SCAN FINGERPRINT',
    ['ui_waiting'] = 'Waiting for target...',
    ['ui_waiting_sub'] = 'Target must press fingerprint scanner',
    ['ui_scan_result'] = 'SCAN RESULT',
    ['ui_match_found'] = 'MATCH FOUND',
    ['ui_first_name'] = 'First Name',
    ['ui_last_name'] = 'Last Name',
    ['ui_gender'] = 'Gender',
    ['ui_dob'] = 'DOB',
    ['ui_back_to_mdt'] = '← BACK TO MDT',
    ['ui_fingerprint_required'] = 'FINGERPRINT REQUIRED',
    ['ui_hold_scanner'] = 'Hold the scanner above for %s seconds',
    ['ui_scan_complete'] = 'Scan complete!',
    ['ui_cancel'] = '✕ CANCEL',
    ['ui_reset_position'] = '↺ RESET POSITION',
}
```

**German translation:**

```lua
Config.Locale = {
    ['no_permission'] = 'Du hast keine Berechtigung, dies zu verwenden.',
    ['no_player_nearby'] = 'Kein Spieler in der Nähe.',
    ['scanning'] = 'Fingerabdruck wird gescannt...',
    ['scan_complete'] = 'Fingerabdruckscan abgeschlossen.',
    ['scan_cancelled'] = 'Scan abgebrochen.',
    ['too_far'] = 'Ziel ist zu weit entfernt.',
    ['target_too_far'] = 'Ziel hat sich zu weit entfernt.',
    ['target_cancelled'] = 'Ziel hat den Scan abgebrochen.',
    ['you_too_far'] = 'Du hast dich zu weit entfernt.',
    
    ['ui_authenticate'] = 'Authentifizieren',
    ['ui_scanning_biometrics'] = 'Biometrie wird gescannt...',
    ['ui_access_granted'] = 'Zugang gewährt',
    ['ui_access_denied'] = 'Zugang verweigert',
    ['ui_mdt_system'] = 'MDT SYSTEM',
    ['ui_officer'] = 'Beamter',
    ['ui_scan_history'] = 'SCAN-VERLAUF',
    ['ui_no_records'] = 'Keine Einträge gefunden',
    ['ui_scan_fingerprint'] = 'FINGERABDRUCK SCANNEN',
    ['ui_waiting'] = 'Warte auf Ziel...',
    ['ui_waiting_sub'] = 'Ziel muss den Scanner bestätigen',
    ['ui_scan_result'] = 'SCAN-ERGEBNIS',
    ['ui_match_found'] = 'TREFFER GEFUNDEN',
    ['ui_first_name'] = 'Vorname',
    ['ui_last_name'] = 'Nachname',
    ['ui_gender'] = 'Geschlecht',
    ['ui_dob'] = 'Geb.-Datum',
    ['ui_back_to_mdt'] = '← ZURÜCK ZUM MDT',
    ['ui_fingerprint_required'] = 'FINGERABDRUCK ERFORDERLICH',
    ['ui_hold_scanner'] = 'Scanner %s Sekunden halten',
    ['ui_scan_complete'] = 'Scan abgeschlossen!',
    ['ui_cancel'] = '✕ ABBRECHEN',
    ['ui_reset_position'] = '↺ POSITION ZURÜCKSETZEN',
}
```

**Spanish translation:**

```lua
Config.Locale = {
    ['no_permission'] = 'No tienes permiso para usar esto.',
    ['no_player_nearby'] = 'No hay jugador cercano.',
    ['scanning'] = 'Escaneando huella dactilar...',
    ['scan_complete'] = 'Escaneo de huella completo.',
    ['scan_cancelled'] = 'Escaneo cancelado.',
    ['too_far'] = 'El objetivo está demasiado lejos.',
    ['target_too_far'] = 'El objetivo se alejó demasiado.',
    ['target_cancelled'] = 'El objetivo canceló el escaneo.',
    ['you_too_far'] = 'Te alejaste demasiado.',
    
    ['ui_authenticate'] = 'Autenticar',
    ['ui_scanning_biometrics'] = 'Escaneando biometría...',
    ['ui_access_granted'] = 'Acceso Concedido',
    ['ui_access_denied'] = 'Acceso Denegado',
    ['ui_mdt_system'] = 'SISTEMA MDT',
    ['ui_officer'] = 'Oficial',
    ['ui_scan_history'] = 'HISTORIAL DE ESCANEOS',
    ['ui_no_records'] = 'No se encontraron registros',
    ['ui_scan_fingerprint'] = 'ESCANEAR HUELLA',
    ['ui_waiting'] = 'Esperando objetivo...',
    ['ui_waiting_sub'] = 'El objetivo debe confirmar el escaneo',
    ['ui_scan_result'] = 'RESULTADO DEL ESCANEO',
    ['ui_match_found'] = 'COINCIDENCIA ENCONTRADA',
    ['ui_first_name'] = 'Nombre',
    ['ui_last_name'] = 'Apellido',
    ['ui_gender'] = 'Género',
    ['ui_dob'] = 'F. Nacimiento',
    ['ui_back_to_mdt'] = '← VOLVER AL MDT',
    ['ui_fingerprint_required'] = 'HUELLA DACTILAR REQUERIDA',
    ['ui_hold_scanner'] = 'Mantén el escáner %s segundos',
    ['ui_scan_complete'] = '¡Escaneo completo!',
    ['ui_cancel'] = '✕ CANCELAR',
    ['ui_reset_position'] = '↺ REINICIAR POSICIÓN',
}
```

***

### 10) Notification System

```lua
Config.UseCustomNotify = true
Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        exports['risk-notify']:Notify({
            type     = ntype,
            title    = title,
            message  = text,
            duration = time or 8000
        })
    end,
}
```

**To use default GTA notifications:**

```lua
Config.UseCustomNotify = false
```

**ox\_lib:**

```lua
Config.UseCustomNotify = true
Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        lib.notify({
            title = title,
            description = text,
            type = ntype,
            duration = time
        })
    end,
}
```

**mythic\_notify:**

```lua
Config.UseCustomNotify = true
Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        exports['mythic_notify']:DoHudText(ntype, text)
    end,
}
```

***

### 11) Database

The script automatically creates the following table on first start — **no manual setup required:**

```sql
CREATE TABLE IF NOT EXISTS `risk_fingerprint_history` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,
    `officer_identifier` VARCHAR(60) NOT NULL,
    `officer_name` VARCHAR(100) NOT NULL,
    `target_firstname` VARCHAR(50) NOT NULL,
    `target_lastname` VARCHAR(50) NOT NULL,
    `target_gender` VARCHAR(10) NOT NULL,
    `target_dob` VARCHAR(20) NOT NULL,
    `scanned_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

**What is stored:**

* Officer's license identifier
* Officer's full name
* Target's first name, last name, gender, date of birth
* Timestamp of each scan

**History limits:**

* Each officer sees their own **last 50 scans**
* Officers can delete individual history entries from the UI
* History is **per-officer** (not shared between officers)

***

### 12) Complete Configuration Examples

#### Example 1: Minimal Setup (Command Only)

```lua
Config.UseCommand = true
Config.Command = 'fingerprint'
Config.UseItem = false
Config.UseAnimation = false
Config.ScanHoldDuration = 5000
Config.MaxDistance = 3.0

Config.AllowedJobs = {
    ['police'] = true,
}

Config.DepartmentName = 'POLICE'
Config.DepartmentSub = 'Fingerprint Scanner'

Config.UseCustomNotify = false
```

#### Example 2: Full RP Setup (Item + Animation)

```lua
Config.UseCommand = false        -- Item only
Config.UseItem = true
Config.ItemName = 'fingerprint_scanner'
Config.UseAnimation = true
Config.AnimDict = 'amb@world_human_security_shine_torch@male@base'
Config.AnimName = 'base'
Config.AnimFlag = 49
Config.ScanHoldDuration = 15000  -- 15 second scan

Config.MaxDistance = 2.5

Config.AllowedJobs = {
    ['police'] = true,
    ['sheriff'] = true,
    ['detective'] = true,
}

Config.DepartmentName = 'LSPD'
Config.DepartmentSub = 'Mobile Data Terminal'

Config.UseCustomNotify = true
```

#### Example 3: Multi-Department Server

```lua
Config.UseCommand = true
Config.Command = 'fp'
Config.UseItem = true
Config.ItemName = 'fingerprint_scanner'
Config.UseAnimation = true
Config.ScanHoldDuration = 10000
Config.MaxDistance = 3.0

Config.AllowedJobs = {
    ['police'] = true,
    ['sheriff'] = true,
    ['lspd'] = true,
    ['bcso'] = true,
    ['sasp'] = true,
    ['ranger'] = true,
    ['fib'] = true,
    ['detective'] = true,
}

Config.DepartmentName = 'LAW ENFORCEMENT'
Config.DepartmentSub = 'Biometric Identification System'

Config.UseCustomNotify = true
```

***

### 13) How It Works

#### Officer Flow

1. **Officer uses item or command** → Scanner UI opens
2. **Officer clicks "Authenticate"** → Job is verified server-side
3. **Access Granted** → Officer can see MDT and scan history
4. **Officer clicks "Scan Fingerprint"** → Finds nearest player within `MaxDistance`
5. **Animation plays** → Server opens scanner UI for target
6. **Target holds button** for `ScanHoldDuration` milliseconds
7. **Scan completes** → Officer sees full identity (name, gender, DOB)
8. **Result saved** to officer's personal scan history

#### Target Flow

1. **Officer initiates scan** → Target receives UI prompt
2. **Target holds the fingerprint button** for required duration
3. **Target can cancel** at any time (officer is notified)
4. **If target moves too far** → scan auto-cancels for both players

#### Security Features

* ✅ Job verified **server-side** (cannot be spoofed from client)
* ✅ Distance checked **server-side** before scan starts
* ✅ Distance monitored **continuously** during scan
* ✅ Resource name verification (must be named `risk-fingerprint`)
* ✅ History entries can only be deleted by the officer who created them
* ✅ Player disconnect handled (active sessions cleaned up automatically)

***

### 14) Troubleshooting

#### ❌ "You do not have permission to use this"

**Cause:** Player's job not in `Config.AllowedJobs`\
**Solution:**

* Check job name in database/framework files
* Add job to `Config.AllowedJobs` with exact spelling
* Job names are case-sensitive

#### ❌ "No player nearby"

**Cause:** No player within `Config.MaxDistance`\
**Solution:**

* Increase `Config.MaxDistance`
* Stand directly next to the target player
* Default is 3.0 meters

#### ❌ Scanner opens but shows "Access Denied"

**Cause:** Job check failed\
**Solution:**

* Verify `Config.AllowedJobs` includes your exact job name
* Check server console for framework detection message
* Restart resource and relog

#### ❌ Item doesn't work

**Possible causes:**

* Item not added to inventory database
* `Config.UseItem = false`
* Wrong `Config.ItemName`

**Solution:**

* Follow item setup in Section 1
* Set `Config.UseItem = true`
* Ensure `Config.ItemName = 'fingerprint_scanner'` (exact match)

#### ❌ Target never receives scan prompt

**Possible causes:**

* Officer not close enough
* Server-side distance check failed
* baseevents not started

**Solution:**

* Officer must be within `Config.MaxDistance`
* Check server console for errors
* Ensure both players are on the server

#### ❌ Scan history not saving

**Possible causes:**

* oxmysql not started
* Database table creation failed
* Wrong table structure

**Solution:**

* Ensure `oxmysql` is started before `risk-fingerprint`
* Check server console for database errors
* Verify `ensure oxmysql` is above `ensure risk-fingerprint` in server.cfg

#### ❌ Animation not playing

**Cause:** Invalid animation dictionary or clip name\
**Solution:**

* Set `Config.UseAnimation = false` to disable
* Use a valid `AnimDict` and `AnimName`
* Check FiveM animation list for valid entries

#### ❌ "Resource must be named 'risk-fingerprint'" warning

**Cause:** Resource folder renamed\
**Solution:**

* Rename the resource folder back to `risk-fingerprint`
* Update `server.cfg` to `ensure risk-fingerprint`
* Restart server

***

### 15) Best Practices

✅ **Use item activation for RP servers** — More immersive than command\
✅ **Keep MaxDistance realistic** — 2.0-3.0 meters feels natural\
✅ **Set ScanHoldDuration based on server type** — Longer for RP, shorter for action\
✅ **Customize department name/sub** — Match your server's lore\
✅ **Test with correct job names** — Case-sensitive, must match exactly\
✅ **Only give item to authorized staff** — Don't give to all players\
✅ **Customize UI text for your language** — Full localization supported\
✅ **Keep oxmysql running** — Required for history and identity lookup

***

### 16) Quick Start Checklist

* \[ ] Add item to your inventory system (Section 1)
* \[ ] Set `Config.AllowedJobs` to your police job names
* \[ ] Set `Config.DepartmentName` and `Config.DepartmentSub`
* \[ ] Configure `Config.UseCommand` and/or `Config.UseItem`
* \[ ] Set `Config.ScanHoldDuration` to your preference
* \[ ] Configure notification system
* \[ ] Add `ensure oxmysql` above `ensure risk-fingerprint` in server.cfg
* \[ ] Restart server and test with a police player
* \[ ] Verify scan history saves correctly

***

**Version:** 1.0.0\
**Framework Support:** ESX, QBCore, QBox\
**Database:** oxmysql (required)\
**Dependencies:** oxmysql


---

# 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://risk-scripts.gitbook.io/risk-scripts/scripts/fingerprint-scanner.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.
