# BK Development Documentation

Welcome to the official documentation for **BK Development’s FiveM Store**.\
This guide will help you install, configure, and get the most out of our scripts.

#### What’s Inside

* Step-by-step installation instructions
* Configuration and customization options
* Troubleshooting and FAQ

#### Getting Started

If this is your first time here, start with the **Installation** sections. Once you’re set up, you can explore configuration and advanced features to tailor everything to your server’s needs.

#### Support

Need help or have questions? Join our [**Discord server**](https://discord.com/invite/QH3g2BG6Z5) to get support directly from our team and community.

Thank you for choosing **BK Development**.

<figure><img src="/files/bAmEnggiqcmOBCQYHXV5" alt=""><figcaption></figcaption></figure>


# FiveM Escrow System

Explanation of how the FiveM Escrow System works and the possible errors you can encounter while using it.

### Differences between escrowed and open-source script

{% tabs %}
{% tab title="Escrowed script" %}

1. Scripts are encrypted with the FiveM Asset Escrow system.
2. Partially available code.
3. You can access everything except server.lua and client.lua.
4. If you wish to add support for custom scripts or frameworks, you can do so in the customizable bridge files.
   {% endtab %}

{% tab title="Open-Source script" %}

1. Every part of the script code is accessible.
2. More customization options.
   {% endtab %}
   {% endtabs %}

{% hint style="info" %}
For more information, visit this [link](https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151)
{% endhint %}

***

### Common errors

<details>

<summary>You lack the required entitlement</summary>

**Information about the error:** This error occurs when there's an issue with the server key or the account used to purchase or activate a resource.

\
**Error fix:**

1. **Verify Server Key:** Ensure your server is using the correct server key linked to the FiveM account that purchased the resource. The server key must match the account that owns the resource.
2. **Check Account Ownership:** Make sure the FiveM account logged into [Portal](https://portal.cfx.re/login) owns the resource. If it's not visible, it may have been purchased with a different account or transferred.
3. **Server Key Management:** If the server key isn't generated by your account, either create a new key or transfer the resource to the account holding the key. <mark style="color:red;">Note that escrowed resources can only be transferred once.</mark>
4. **Restart Server:** A server restart can often resolve the issue.

</details>

<details>

<summary>Failed to verify protected resource</summary>

**Information about the error:** Script files were possibly corrupted during transfer. \
\
**Error fix:**

1. **Check for the .fxap File**: Ensure that the `.fxap` file is present in the script folder. This file can sometimes be skipped during transfer.
2. **FTP transferring problems**: FileZilla is known to cause issues with encrypted files, so double-check for missing files.
3. **Restart server:**  A server restart can often resolve the issue.

</details>

<details>

<summary>Error parsing script / Failed to load script</summary>

**Information about the error:** Your server artifacts are likely outdated.

**Error fix:**

1. Update your server to version **5181** or above.
2. Some files are corrupted or missing. Try re-uploading the script with another FTP.

</details>


# bk\_politicalsystem

Introducing the **BK Political System**, a complete system designed for a next-level political roleplay in your server. This powerful script brings realism and depth to in-game governance, allowing players to experience the complexities of political life. With the ability to control elections, manage taxes, and create laws, players can immerse themselves in a dynamic, interactive political environment.

* **Control Election Process**: Players with the required job can initiate, manage and terminate elections, exclude political parties from participating, and reset votes for individual players.
* **Parliament Functions**: Those holding parliamentary jobs, such as Chairman of the Parliament or deputies, can engage in both legislative decision-making and tax regulation. The chairman organizes votes among deputies, through a UI menu. Deputies cast votes on laws and tax changes.
* **Tax Management**: This system includes an income tax and VAT that can be imposed on players. The President, in coordination with deputies (through voting), can adjust these tax rates. This dynamic taxation system ensures that in-game economies reflect real-world government functions, contributing to realism​. You are able to integrate the tax system into many of your resources.
* **Law Creation and Management**: Authorized players, such as the president, can propose, edit, or abolish laws directly from a user-friendly UI. This allows for easy legislative adjustments and keeps the in-game legal system flexible and responsive to player needs.
* **Political Parties**: Players are able to register their own political parties or join existing ones. Political parties must be approved by the president in order to participate in elections. The chairman of the party can manage the political parties and the members in it. This functionality immerses players in a dynamic political experience, allowing them to shape the in-game political life actively.
* **UI Integration**: The system includes intuitive menus for voting (both civilian and deputy) and law management. The interface simplifies complex governmental tasks for all authorized players​.

**Political Jobs**:

* **President**: Manage elections, approve or deny political parties, hire or fire deputies, and manage the national treasury and tax rates.
* **Chairman of the parliament**: Controls parliamentary votes and convenes legislative sessions.
* **Deputies**: Participate in voting on laws, taxes, and other governmental matters.

**Additional Features**:

* **National Treasury**: Managed by the president, it centralizes the control of government funds, taxes, and expenses.
* **Email System**: Allows direct communication with deputies, with phone integration, ensuring effective in-game governance.
* **Discord Webhooks**
* Supports **ESX/QB/Qbox/Custom** frameworks&#x20;
* Highly **configurable** and **optimized**

The script is protected with [FiveM Asset Escrow system](https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151)

<figure><img src="/files/dVsjau4qm9zkftRNSS0j" alt=""><figcaption></figcaption></figure>


# Dependencies

A list of required resources to run BK Political System.

**QB/Qbox Dependencies:**

<table><thead><tr><th width="369" align="center">Dependency</th><th width="206" align="center">Information</th><th>Requirement Level</th></tr></thead><tbody><tr><td align="center"><a href="https://github.com/qbcore-framework/qb-target">qb-target</a>/<a href="https://github.com/CommunityOx/ox_target">ox_target</a>/custom</td><td align="center">Used for 3-rd eye</td><td>Optional</td></tr><tr><td align="center"><a href="https://github.com/qbcore-framework/qb-menu">qb-menu</a>/<a href="https://github.com/CommunityOx/ox_lib">ox_lib</a>/custom</td><td align="center">Used for the menus in the script</td><td>Required</td></tr><tr><td align="center"><a href="https://github.com/qbcore-framework/qb-input">qb-input</a>/<a href="https://github.com/CommunityOx/ox_lib">ox_lib</a>/custom</td><td align="center">Used for the input options in the script</td><td>Required</td></tr><tr><td align="center"><a href="https://github.com/qbcore-framework/qb-phone">qb-phone</a>/<a href="https://store.lbphone.com/">LB-phone</a>/<a href="https://fivem.gkshop.org/category/script">GKSPHONE</a>/<a href="https://www.quasar-store.com/">qs-smartphone</a>/<a href="https://scripts.teamsgg.dev/package/5950013">YSeries Phone</a>/<a href="https://okok.tebex.io/package/6123242">okokPhone</a>/<a href="https://jpresources.tebex.io/category/qbcore">JPRPhone</a>/<a href="https://fivem.roadshop.org/package/4885785">roadphone</a>/qbx_npwd/custom</td><td align="center">Used for the email features in the script.</td><td>Required</td></tr></tbody></table>

**ESX Dependencies:**

<table><thead><tr><th width="368" align="center">Dependency</th><th width="205" align="center">Information</th><th>Requirement Level</th></tr></thead><tbody><tr><td align="center"><a href="https://github.com/CommunityOx/ox_target">ox_target</a>/custom</td><td align="center">Used for 3-rd eye</td><td>Optional</td></tr><tr><td align="center"><a href="https://github.com/CommunityOx/ox_lib">ox_lib</a>/custom</td><td align="center">Used for the menus and the input options in the script</td><td>Required</td></tr><tr><td align="center"><a href="https://store.lbphone.com/">LB-phone</a>/<a href="https://fivem.gkshop.org/category/script">GKSPHONE</a>/<a href="https://www.quasar-store.com/">qs-smartphone</a>/<a href="https://scripts.teamsgg.dev/package/5950013">YSeries Phone</a>/<a href="https://okok.tebex.io/package/6123242">okokPhone</a>/<a href="https://jpresources.tebex.io/category/esx">JPRPhone</a>/<a href="https://fivem.roadshop.org/package/4885785">roadphone</a>/qbx_npwd/custom</td><td align="center">Used for the email features in the script.</td><td>Required</td></tr></tbody></table>

{% hint style="warning" %}
There’s a chance some of these scripts might already be installed on your server. Installing duplicates can cause conflicts or errors, so always check your existing resources before adding new ones.
{% endhint %}

{% hint style="warning" %}
If you are using custom framework, you have to make it work with the script. We provide limited or no support for custom frameworks!
{% endhint %}


# Installation

Step-by-step guide to adding the BK Political System to your server.

{% tabs %}
{% tab title="ESX" %}

1. Install [all dependencies](/scripts/bk_politicalsystem/dependencies) required to run the  script.

> Before installing, check if you already have them, because they are usually installed by default.
>
> <mark style="color:red;">NOTE</mark>: Be sure that you have the latest versions of the dependencies.

2. Go to your [FiveM Portal](https://portal.cfx.re/login) and download `bk_politicalsystem`.

> After downloading, extract `bk_politicalsystem.`

3. Drag and drop `bk_politicalsystem` into your resources directory and add it in your `server.cfg`.
4. Configure your script.

> Be sure to configure these options:
>
> ```lua
> Config.Interface = {
>     Menu = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Input = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Notifications = 'ox', -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     TextUI = 'ox' -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
> }
> ```

5. Insert the following SQL to your database.

> ```sql
> INSERT INTO `jobs` (name, label) VALUES
>     ('politician', 'Politician');
>
> INSERT INTO `job_grades` (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
>     ('politician',0,'deputy','Deputy',200,'{}','{}'),
>     ('politician',1,'chairman','Chairman of the Parliament',300,'{}','{}'),
>     ('politician',2,'president','President',500,'{}','{}');
> ```

6. If you want to use the tax system, follow the installation guide provided [here](/scripts/bk_politicalsystem/exports).
7. Start/Restart your server.
   {% endtab %}

{% tab title="QB" %}

1. Install [all dependencies](/scripts/bk_politicalsystem/dependencies) required to run the  script.

> Before installing, check if you already have them, because they are usually installed by default.
>
> <mark style="color:red;">NOTE</mark>: Be sure that you have the latest versions of the dependencies.

2. Go to your [FiveM Portal](https://portal.cfx.re/login) and download `bk_politicalsystem`.

> After downloading, extract `bk_politicalsystem.`

3. Drag and drop `bk_politicalsystem` into your resources directory and add it in your `server.cfg`.
4. Add the job to qb-core/shared/jobs.lua.

> ```lua
> politician = { label = 'Politician', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Deputy', payment = 750}, ['1'] = {name = 'Chairman of the parliament', payment = 1500}, ['2'] = {name = 'President', payment = 2500, isboss = true} } },
> ```

5. Configure your script.

> Be sure to configure these options:
>
> ```lua
> Config.Interface = {
>     Menu = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Input = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Notifications = 'ox', -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     TextUI = 'ox' -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
> }
> ```

6. If you want to use the tax system, follow the installation guide provided [here](/scripts/bk_politicalsystem/exports).
7. Start/Restart your server.
   {% endtab %}

{% tab title="Qbox" %}

1. Install [all dependencies](/scripts/bk_politicalsystem/dependencies) required to run the  script.

> Before installing, check if you already have them, because they are usually installed by default.
>
> <mark style="color:red;">NOTE</mark>: Be sure that you have the latest versions of the dependencies.

2. Go to your [FiveM Portal](https://portal.cfx.re/login) and download `bk_politicalsystem`.

> After downloading, extract `bk_politicalsystem.`

3. Drag and drop `bk_politicalsystem` into your resources directory and add it in your `server.cfg`.
4. Add the job to `qbx_core/shared/jobs.lua`:

> ```lua
> ['politician'] = {
>     label = 'Politician',
>     defaultDuty = true,
>     offDutyPay = false,
>     grades = {
>         [0] = { name = 'Deputy', payment = 750 },
>         [1] = { name = 'Chairman of the Parliament', payment = 1500 },
>         [2] = { name = 'President', isboss = true, payment = 2500 },
>     }
> },
>
> ```

5. Configure your script.

> Be sure to configure these options:
>
> ```lua
> Config.Interface = {
>     Menu = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Input = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     Notifications = 'ox', -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
>     TextUI = 'ox' -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
> }
> ```

6. If you want to use the tax system, follow the installation guide provided [here](/scripts/bk_politicalsystem/exports).
7. Start/Restart your server.
   {% endtab %}

{% tab title="Custom" %}

1. Go to your FiveM Portal and download bk\_politicalsystem.
2. Drag and drop bk\_politicalsystem into your resources directory and add it in your server.cfg.
3. Go to configs/config.lua and put your framework to 'custom'
4. Go to bridges/custom/sv\_custom\_bridge.lua and cl\_custom\_bridge.lua and setup your custom framework there based on the esx/qb bridge
5. If you want to use the tax system, follow the installation guide provided [here](/scripts/bk_politicalsystem/exports).
6. Start/Restart your server.
   {% endtab %}
   {% endtabs %}

{% hint style="info" %}
For support,  join our [discord](https://discord.gg/wZryDXaCAU)
{% endhint %}


# Configuration

Available configuration options to customize the script.

{% code title="config.lua" %}

```lua
Config = {Locales = {}}

Config.Framework = 'auto' -- 'auto', 'qb' (use qb for qbox), 'esx', 'custom' -- setup custom in bridges/custom/custom_bridge.lua

--[[
    Only required if Config.Framework = 'esx'
    Options:
        'new' -- v1.8.5-latest
        'old' -- v1 - v.1.8.5
]]
Config.ESXVersion = 'new'

Config.Phone = 'auto' -- options: 'auto', 'qb-phone', 'qbx_npwd' 'quasar', 'gksphone', 'lbphone', 'yflip', 'okokPhone', 'jpr-phonesystem', 'roadphone', 'custom' -- for mail -- setup custom in config/sv_customizable_functions.lua

Config.Interface = {
    Menu = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
    Input = 'ox', -- options 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
    Notifications = 'ox', -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
    TextUI = 'ox' -- options 'esx', 'qb', 'ox', 'custom' -- editable in cl_customizable_functions.lua
}

Config.Zones = 'ox' -- options 'PolyZone', 'ox', 'custom' -- editable in cl_customizable_functions.lua

Config.Target = false  -- true/false

Config.DeputyVoting = { -- additional setting for deputy voting to allow or prohibit president and chairman from vote on deputy votes
    president = true, -- true if you want to allow the president to vote in deputies votings, false if you don't want to.
    chairman = true -- true if you want to allow the chairman to vote in deputies votings, false if you don't want to.
}

Config.Translation = 'en' -- check in 'locales' folder

Config.TaxSystem = 1 -- option 1 = tax not included in price, option 2 = tax included in price || example for 20% tax: (1 = player will pay 12$ for 10$ product) (2 = player will pay 10$ for 10$ product but shop recieves 8$)

Config.PartyManagement = { -- commands for the politcal parties
    manageParty = "managepp",
    checkMyParty = "pp",
    leaveParty = "leavepp"
}

Config.LawsCommand = { -- command and keybind for opening the laws menu. Keybind can be changed in-game from the FiveM options menu!
    commandName = 'laws',
    keyBind = 'B',
}

Config.Blips = {
    townhall = {
        show = true,
        sprite = 525, -- https://docs.fivem.net/docs/game-references/blips/
        color = 0, -- https://docs.fivem.net/docs/game-references/blips/
        size = 0.8, -- has to be in decimals
        label = 'Town Hall',
        coords = vector3(-556.03, -193.58, 38.23)
    }
}

Config.Icons = { -- check compatible icons depending on what interface (menu/input) you use - default https://fontawesome.com (compatible with ox/qb)
    ['Manage Members'] = 'fas fa-users',
    ['Create Election'] = 'fas fa-users',
    ['End Election'] = 'fas fa-vote-yea',
    ['Ban From Elections'] = 'fas fa-ban',
    ['Unban From Elections'] = 'fas fa-check-square',
    ['Clear Player Votes'] = 'fas fa-eraser',
    ['Announcement'] = 'fas fa-paper-plane',
    ['Hire Deputy'] = 'fas fa-briefcase',
    ['Fire Deputy'] = 'fas fa-sign-out-alt',
    ['Money Management'] = 'fas fa-university',
    ['Money Deposit'] = 'fas fa-donate',
    ['Money Withdraw'] = 'fas fa-money-bill-wave',
    ['Election Options'] = 'fas fa-print',
    ['Political Parties Options'] = 'fas fa-user-slash',
    ['Other Options'] = 'fas fa-paper-plane',
    ['President Menu'] = 'fas fa-flag',
    ['Conference Menu'] = 'fas fa-book-open',
    ['Conference Menu Title'] = 'fas fa-bookmark',
    ['Party Application'] = 'fas fa-book-open',
    ['Party Application Title'] = 'fas fa-bookmark',
    ['Party Applications List'] = 'fas fa-print',
    ['Voting Menu'] = 'fas fa-print',
    ['Party Rename'] = 'fas fa-pen',
    ['Party Disband'] = 'fas fa-trash',
    ['Manage Political Parties'] = 'fas fa-list',
    ['Tax Management'] = 'fas fa-money-bill',
    ['Income Tax'] = 'fas fa-percent',
    ['VAT Tax'] = 'fas fa-percent'
}

Config.Job = {
    jobname = 'politician',
    joblabel = 'Politician',
    grades = { --- do not change keys (president, chairman, deputy), only change numbers of grades (if wanted), and keep the order of numbers from lower perms to higher perms
        president = 2,
        chairman = 1,
        deputy = 0
    }
}

Config.PresidentMenu = {
    targetCoords = vector3(-528.66, -188.68, 42.95),
    pedHeading = 121.93,
}

Config.ConferenceRoom = {
    targetCoords = vector3(-534.69, -178.28, 43.80), -- location of the conference menu
    roomCoords = vector3(-537.2, -179.84, 44.26), -- put the center of the room
    radius = 15, -- the radius from the room coords where deputies can vote ( we don't want someone who is not in the hall to vote so that's why we are using this )
}

Config.Application = {
    ped = {
        pedModel = 'cs_bankman',
        pedCoords = vector3(-542.43, -197.23, 38.24),
        pedHeading = 77.25,
    },
}

Config.VotingLocation = {
    {
        points = { -- acts as points for polyzone/qb-target/ox_target/ox_lib
            vector3(-541.32, -183.47, 37.23),
            vector3(-543.91, -179.28, 37.23),
            vector3(-542.76, -178.18, 37.23),
            vector3(-540.39, -182.63, 37.23)
        },
        label = 'Vote'
    },
    {
        points = { -- acts as points for polyzone/qb-target/ox_target/ox_lib
            vector3(-535.76, -174.53, 37.23),
            vector3(-533.43, -178.4, 37.23),
            vector3(-534.19, -173.66, 37.23),
            vector3(-531.93, -177.52, 37.23)
        },
        label = 'Vote',
        coords = {
            vector3(-542.92, -179.23, 38.23),
            vector3(-542.11, -180.59, 38.23),
            vector3(-541.32, -181.97, 38.23)
        }
    }
}

function Translate(msg, ...)
    if Config.Locales[Config.Translation] then
        if Config.Locales[Config.Translation][msg] then
            return string.format(Config.Locales[Config.Translation][msg], ...)
        elseif Config.Translation ~= 'en' and Config.Locales['en'] and Config.Locales['en'][msg] then
            return string.format(Config.Locales['en'][msg], ...)
        else
            return 'Translation ['.. Config.Translation ..'] ['..msg..'] does not exist.'
        end
    elseif Config.Translation ~= 'en' and Config.Locales['en'] and Config.Locales['en'][msg] then
        return string.format(Config.Locales['en'][msg], ...)
    else
        return 'Translation ['.. Config.Translation .. '] not found.'
    end
end

local function detectFramework()
    if (GetResourceState('qb-core') == 'started') then return 'qb' end
    if (GetResourceState('qbox') == 'started') then return 'qb' end
    if (GetResourceState('es_extended') == 'started') then return 'esx' end
    return 'custom'
end

if Config.Framework == 'auto' then Config.Framework = detectFramework() end

local function detectPhone()
    if (GetResourceState('qb-phone') == 'started') then return 'qb-phone' end
    if (GetResourceState('qs-smartphone') == 'started') then return 'quasar' end
    if (GetResourceState('gksphone') == 'started') then return 'gksphone' end
    if (GetResourceState('lb-phone') == 'started') then return 'lbphone' end
    if (GetResourceState('yflip-phone') == 'started') then return 'yflip' end
    if (GetResourceState('okokPhone') == 'started') then return 'okokPhone' end
    if (GetResourceState('jpr-phonesystem') == 'started') then return 'jpr-phonesystem' end
    if (GetResourceState('roadphone') == 'started') then return 'roadphone' end
    if (GetResourceState('qbx_npwd') == 'started') then return 'qbx_npwd' end
    return 'custom'
end

if Config.Phone == 'auto' then Config.Phone = detectPhone() end
```

{% endcode %}

<details>

<summary>webhooks.lua</summary>

```lua
Webhooks = {}

Webhooks.Array = {
    civilian_voting = '',
    deputies_voting = ''
}

Webhooks.BotName = 'Mr. Politics'
Webhooks.Header = 'Political System'
Webhooks.ProfilePicture = 'https://cdn.discordapp.com/attachments/1204869666493829184/1214581454651138088/BK.png?ex=65f9a220&is=65e72d20&hm=614a763e06556de7b8912775e0f5f7b2b18c8d1d9079edb4e5dfb370e206ada9&'

--[[
    blue = 255,
    red = 16711680,
    green = 65280,
    white = 16777215,
    black = 0,
    orange = 16744192,
    yellow = 16776960,
    pink = 16761035,
    lightgreen = 65309,
]]
Webhooks.Color = '255'
```

</details>

<details>

<summary>cl_customizable_functions.lua</summary>

```lua
function Notification(msg, type, duration)
    if Config.Interface.Notifications == 'qb' then
        QBCore.Functions.Notify(msg, type, duration)
    elseif Config.Interface.Notifications == 'ox' then
        lib.notify({id = msg, title = Translate('notificationTitle'), description = msg, duration = duration, type = type})
    elseif Config.Interface.Notifications == 'esx' then
        ESX.ShowNotification(msg)
    elseif Config.Interface.Notifications == 'custom' then
        -- your custom function
    end
end

function ShowTextUi(msg)
    if Config.Interface.TextUI == 'qb' then
        exports['qb-core']:DrawText(msg, 'right')
    elseif Config.Interface.TextUI == 'ox' then
        lib.showTextUI(msg)
    elseif Config.Interface.TextUI == 'esx' then
        ESX.TextUI(msg)
    elseif Config.Interface.TextUI == 'custom' then
        -- your custom function
    end
end

function HideTextUi()
    if Config.Interface.TextUI == 'qb' then
        exports['qb-core']:HideText()
    elseif Config.Interface.TextUI == 'ox' then
        lib.hideTextUI()
    elseif Config.Interface.TextUI == 'esx' then
        ESX.HideUI()
    elseif Config.Interface.TextUI == 'custom' then
        -- your custom function
    end
end

function Menu(arg)
    if Config.Interface.Menu == 'qb' then
        return exports['qb-menu']:openMenu(arg)
    elseif Config.Interface.Menu == 'ox' then
        return oxlibMenuHandler(arg)
    elseif Config.Interface.Menu == 'custom' then
        -- your custom function
    end
end

function Input(arg)
    if Config.Interface.Input == 'qb' then
        return exports['qb-input']:ShowInput(arg)
    elseif Config.Interface.Input == 'ox' then
        return oxlibInputHandler(arg)
    elseif Config.Interface.Input == 'custom' then
        -- your custom function
    end
end

function AddCircleZone(name, coords, radius, options1, options2)
    return exports['qb-target']:AddCircleZone(name, coords, radius, options1, options2) --qb-target/ox_target
end

function AddTargetEntity(name, options)
    return exports['qb-target']:AddTargetEntity(name, options) --qb-target/ox_target
end

function AddPolyZone(name, coords, options1, options2)
    return exports['qb-target']:AddPolyZone(name, coords, options1, options2) --qb-target/ox_target
end

function RemoveZone(name)
    pcall(function()
        return exports['qb-target']:RemoveZone(name)
    end) -- qb-target/ox_target
end

function PolyZoneCreation(points, data, playerInsideHandler)
    if Config.Zones == 'PolyZone' then
        local zone = PolyZone:Create(points, data)
        zone:onPlayerInOut(playerInsideHandler)

    elseif Config.Zones == 'ox' then
        local pointsConverted = {}
        for i = 1, #points do
            pointsConverted[i] = vector3(points[i].x, points[i].y, data.minZ + 1)
        end
        lib.zones.poly({
            points = pointsConverted,
            thickness = 4,
            onEnter = function() playerInsideHandler(true) end,
            onExit = function() playerInsideHandler(false) end
        })

    elseif Config.Zones == 'custom' then
        -- your custom zones handler
    end
end

function CircleZoneCreation(coords, radius, data, playerInsideHandler)
    if Config.Zones == 'PolyZone' then
        local zone = CircleZone:Create(coords, radius, data)
        zone:onPlayerInOut(playerInsideHandler)

    elseif Config.Zones == 'ox' then
        lib.zones.sphere({
            coords = coords,
            radius = radius,
            onEnter = function() playerInsideHandler(true) end,
            onExit = function() playerInsideHandler(false) end
        })

    elseif Config.Zones == 'custom' then
        -- your custom zones handler
    end
end
```

</details>

<details>

<summary>sv_customizable_functions.lua</summary>

```lua
function SendEMail(target, title, message)
    if Config.Phone == 'qb-phone' then
        QBphoneEmail(target, title, message)

    elseif Config.Phone == 'quasar' then
        TriggerClientEvent('bk_politicalsystem:client:quasarIntegration', target, title, message)

    elseif Config.Phone == 'gksphone' then
        local mailData = {
            sender = Translate('president_mail'),
            image = '/html/static/img/icons/mail.png',
            subject = title,
            message = message
        }
        exports['gksphone']:SendNewMail(target, mailData)

    elseif Config.Phone == 'lbphone' then
        local phoneNumber = exports['lb-phone']:GetEquippedPhoneNumber(target)
        local email = exports['lb-phone']:GetEmailAddress(phoneNumber)
        exports['lb-phone']:SendMail({
            to = email,
            subject = Translate('president')..' - '..title,
            message = message
        })

    elseif Config.Phone == 'yflip' then
        exports['yflip-phone']:SendMail({
            title = title,
            sender = Translate('president_mail'),
            senderDisplayName = Translate('president'),
            content = message
        }, 'source', target)

    elseif Config.Phone == 'okokPhone' then
        local email = exports['okokPhone']:getEmailAddressFromSource(target)

        exports['okokPhone']:sendEmail({
			sender = Translate('president_mail'),
            recipients = {email},
            subject = Translate('president')..' - '..title,
            body = message,
        })

    elseif Config.Phone == 'jpr-phonesystem' then
        TriggerEvent('jpr-phonesystem:server:sendEmail', {
            subject = title,
            message = message,
            sender = Translate('president_mail'),
            event = {},
        }, target)

    elseif Config.Phone == 'roadphone' then
        local identifier = GetPlayerIdentifierByType(target, "license")
        if identifier then
            local data = {
                sender = Translate('president_mail'),
                subject = title,
                message = message
            }
            exports['roadphone']:sendMailOffline(identifier, data)
        end

    elseif Config.Phone == 'qbx_npwd' then
        local Player = QBCore.Functions.GetPlayer(target)
        if not Player then
            print('[BK][MAIL][NPWD] Player nil for source=', target)
            return
        end
        local cid = Player.PlayerData.citizenid
        TriggerEvent('qb-phone:server:sendNewMailToOffline', cid, {
            sender = Translate('president_mail'),
            subject = title,
            message = message
        })

    elseif Config.Phone == 'custom' then
        -- your custom function
    end
end

function SendDiscordMessage(webhook, title, fields, color)
    if not Webhooks.Array[webhook] then return end

    local webhook = Webhooks.Array[webhook]
    local data = {
        {
            ['title'] = title,
            ['color'] = color or Webhooks.Color,
            ['footer'] = {
                ['text'] = os.date()
            },
            ['fields'] = type(fields) == 'table' and fields or nil,
            ['description'] = type(fields) == 'string' and fields or nil,
            ['author'] = {
                ['name'] = Webhooks.Header,
                ['icon_url'] = Webhooks.ProfilePicture
            }
        }
    }

    PerformHttpRequest(webhook, nil, 'POST', json.encode({
        username = Webhooks.BotName,
        embeds = data
    }), {['Content-Type'] = 'application/json'})
end

```

</details>


# Exports

Available exports for integrating the BK Political System with other scripts.

There are two available server side exports for usage:

```lua
exports['bk_politicalsystem']:AddMoneyIncome(target, account, money)
```

which gives player money and deducts a part based on Income Tax Rate and

```lua
exports['bk_politicalsystem']:RemoveMoneyVAT(target, account, money)
```

which removes player money and deducts money based on VAT Tax Rate

## Arguments

There are 3 arguments

* target - player server ID
* account - account to remove money ('bank', 'money', 'cash', etc.)
* money - amount of money to process

## Example

Example on a shop script:

Before:

{% tabs %}
{% tab title="ESX" %}

```lua
local xPlayer = ESX.GetPlayerFromId(source)
local bread_price = 10

xPlayer.removeAccountMoney('money', bread_price)
xPlayer.addInventoryItem('bread', 1)
```

{% endtab %}

{% tab title="QB/Qbox" %}

```lua
local Player = QBCore.Functions.GetPlayer(source)
local bread_price = 10

Player.Functions.RemoveMoney('cash', bread_price)
exports['qb-inventory']:AddItem(source, 'bread', 1)
```

{% endtab %}
{% endtabs %}

After installation:

{% tabs %}
{% tab title="ESX" %}

```lua
local xPlayer = ESX.GetPlayerFromId(source)
local bread_price = 10

exports['bk_politicalsystem']:RemoveMoneyVAT(source, 'money', bread_price)
xPlayer.addInventoryItem('bread', 1)
```

{% endtab %}

{% tab title="QB/Qbox" %}

```lua
local bread_price = 10

exports['bk_politicalsystem']:RemoveMoneyVAT(source, 'cash', bread_price)
exports['qb-inventory']:AddItem(source, 'bread', 1)
```

{% endtab %}
{% endtabs %}

## Adding income tax into paycheck system

{% tabs %}
{% tab title="ESX" %}

1. Go to es\_extended/server/paycheck.lua
2. Find 1 of this:

{% code fullWidth="true" %}

```lua
xPlayer.addAccountMoney("bank", salary, "Welfare Check")
```

{% endcode %}

and 3 of these

```lua
xPlayer.addAccountMoney("bank", salary, "Paycheck")
```

3. Replace those with

```lua
exports['bk_politicalsystem']:AddMoneyIncome(xPlayer.source, 'bank', salary)
```

4. Ready for usage !
   {% endtab %}

{% tab title="QB/Qbox" %}

1. Go to qb-core/server/functions.lua
2. Go to function on line 398
3. Find 3 of these in that function

```lua
Player.Functions.AddMoney('bank', payment, 'paycheck')
```

4. Replace them with these:

```lua
exports['bk_politicalsystem']:AddMoneyIncome(Player.PlayerData.source, 'bank', payment)
```

5. Ready for usage !
   {% endtab %}
   {% endtabs %}


# RP

Roleplay tips and example scenarios for immersive political gameplay.

If you're looking for a flexible proportional formula for allocating parliamentary seats, a basic proportional method can be adapted for any total number of seats. The formula distributes seats among various parties based on the votes they received, and can be adjusted for any number of available seats in the parliament. Here's how to apply it:

1. Let $$V$$ be the total number of votes.
2. Let $$Vi$$ be the number of votes received by each party $$i$$.
3. Let $$S$$ be the total number of seats in the parliament.
4. For each party, calculate the percentage of the total votes it received:

   $$Pi=Vi/V$$
5. Multiply this percentage $$Pi$$ by the total number of seats $$S$$ to get the approximate number of seats for each party:

   $$Mi=Pi×S$$

   Here, $$Mi$$ might not be a whole number.
6. Allocate seats based on these approximate numbers. If the total number of allocated seats does not add up to $$S$$, adjust by adding or removing seats from parties with the closest values to the next whole number.

{% file src="/files/BdwSLJRobm6x6cvHckFb" %}
Simple model for a constitution of your RP server
{% endfile %}

{% file src="/files/0lIXJDiaWCPiMPmouumg" %}
Simple model for a criminal code of your RP server
{% endfile %}


# bk\_doj

Introducing **BK D.O.J.**, a comprehensive **Department of Justice** system crafted for immersive legal roleplay within your FiveM server. This powerful script simulates a functioning judicial system with criminal and civil court processes, giving players the ability to experience law, order, and courtroom dynamics in a realistic and engaging way.

### Features

#### Court Jobs:

**Lawyer:** Represent civilians in civil or criminal cases; schedule appointments and prepare legal defenses.\
**Prosecutor:** File and handle criminal cases against players, presenting evidence and arguments in court.\
**Judge:** Preside over both criminal and civil cases, issue verdicts, fines, and jail time.\
**Chief Judge:** Oversee all judicial activity, manage judges, lawyers and prosecutors, review cases, and access DOJ administration tools.

#### Case Filing System:

**Civilian Cases:** Any civilian can file a civil case against another player for damages or disputes.\
**Criminal Cases:** Prosecutors can initiate criminal cases based on evidence and reports of illegal activities.\
**Evidence Inventory:** Each case includes a built-in inventory where inventory items can be stored.

#### Citizen Features:

* File new cases against other players.
* Request appointments with lawyers.
* View case status and details at any time.

#### UI Menus:

DOJ Menu\
**Cases:** View and manage active cases with all related details and evidence.\
**Background Check:** Search any player by ID or name for case history.\
**Case Archive:** Access all archived cases for reference.\
**Requests** (Chief Judge Only)**:** Approve or deny case requests and assign judges.\
Public View Menu

* Allows any player to search and view public case information, including charges, verdicts, and involved parties.
* Perfect for transparency in public trials or for news roleplay reporting.
* Displays archived cases for historical reference.

#### Lawyer & Appointment System:

* Lawyers receive appointment requests directly and can manage them through their menu.
* Citizens can request meetings with specific lawyers for representation.

#### Email Integration:

* All major case updates and appointments are sent directly to the in-game email system for instant notifications.

#### Optimized & Configurable:

* Supports **ESX/QB/Qbox/custom** frameworks.
* Fully optimized for performance with high configurability to match your server’s needs.

The script is protected with [FiveM Asset Escrow system](https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151)

<figure><img src="/files/B6cBBtS5p9V9vhD8lGLN" alt=""><figcaption></figcaption></figure>


# Dependencies

A list of required resources to run BK DOJ.

<details>

<summary>Supported Frameworks</summary>

**Supported frameworks:**

* `qbox`
* `qb` (QBCore)  version 1.3.0+
* `esx`
* `custom`

</details>

<details>

<summary><a href="https://github.com/CommunityOx/oxmysql">oxmysql</a></summary>

A high-performance MySQL wrapper for FiveM resources, allowing scripts to efficiently interact with MySQL databases.

</details>

<details>

<summary><a href="https://github.com/CommunityOx/ox_lib">ox_lib</a></summary>

Used for the menus, notifications and the input options in the script.

</details>

<details>

<summary>Phone (recommended for better experience, not required)</summary>

Used to send court-related messages and notifications directly to players.

**Supported phone systems:**

* [`npwd_phone`](https://github.com/project-error/npwd)
* [`qb-phone`](https://github.com/qbcore-framework/qb-phone)
* [`lb-phone`](https://lbscripts.com/phone)
* [`gksphone`](https://fivem.gkshop.org/package/6303685)
* [`yseries`](https://teamsgg.dev/scripts)
* [`jpr-phonesystem`](https://jpresources.tebex.io/)
* [`roadphone`](https://fivem.roadshop.org/package/4885785)
* `custom`

</details>

<details>

<summary>Inventory (recommended for better experience, not required)</summary>

Connects to your inventory system for storing and accessing DOJ evidence, with automatic detection or custom configuration.

**Supported inventory systems:**

* [`ox_inventory`](https://github.com/CommunityOx/ox_inventory)
* [`qb-inventory`](https://github.com/qbcore-framework/qb-inventory)
* [`qs-inventory`](https://www.quasar-store.com/scripts/advancedinventory)
* [`core_inventory`](https://www.c8re.store/)
* [`tgiann-inventory`](https://tgiann.com/en/package/6273000)
* [`ps-inventory`](https://github.com/Project-Sloth/ps-inventory)
* `custom`

</details>

<details>

<summary>Jail System (recommended for better experience, not required)</summary>

Links with your jail resource to automatically send convicted players to prison after court rulings.

**Supported jail systems:**

* [`xt-prison`](https://github.com/xT-Development/xt-prison)
* `custom`

</details>

<details>

<summary>MariaDB 10.5+ or MySQL 5.7.22+</summary>

An open-source database, compatible with MySQL, used in FiveM to store and manage persistent server data like player info, vehicles, and economy records.

</details>

{% hint style="info" %}
**bk\_doj uses Citizen ID (CID)**

To make CID lookups easier for players, we provide a **free lookup script**.\
Download it from [here](https://github.com/BK-Development-FiveM/bk_cidlookup).
{% endhint %}

{% hint style="success" %}
If you need assistance setting up custom scripts alongside our resources, you can open a ticket in our [Discord](https://discord.gg/wZryDXaCAU). While we’ll do our best to help, please note that we can’t guarantee full compatibility if the custom script has issues outside of our control.
{% endhint %}

{% hint style="warning" %}
There’s a chance some of these scripts might already be installed on your server. Installing duplicates can cause conflicts or errors, so always check your existing resources before adding new ones.
{% endhint %}


# Installation

Step-by-step guide to adding the BK DOJ to your server.

{% tabs %}
{% tab title="ESX" %}

1. Place the script in your `resources` folder.
2. Add `ensure bk_doj` to your `server.cfg`. Make sure it starts after the framework resources.
3. Run the following SQL code with your preferred SQL client (HeidiSQL recommended). Make sure you select your framework's database:

```sql
INSERT INTO `addon_account` (name, label, shared) VALUES
	('society_doj', 'D.O.J', 1)
;

INSERT INTO `datastore` (name, label, shared) VALUES
	('society_doj', 'D.O.J', 1)
;

INSERT INTO `jobs` (name, label) VALUES
	('doj', 'D.O.J')
;

INSERT INTO `job_grades` (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
	('doj', 0, 'lawyer', 'Lawyer', 500, '{}', '{}'),
	('doj', 1, 'prosecutor', 'Prosecutor', 500, '{}', '{}'),
	('doj', 2, 'judge', 'Judge', 1000, '{}', '{}'),
	('doj', 3, 'chief_judge', 'Chief Judge', 1500, '{}', '{}')
;
```

4. Modify the [config](/scripts/bk_doj/configuration) to your liking and specific resources.
5. Enjoy!
   {% endtab %}

{% tab title="QB" %}

1. Place the script in your `resources` folder.
2. Add `ensure bk_doj` to your `server.cfg`. Make sure it starts after the your framework resources.
3. Add the following to `qb-core/shared/jobs.lua`:

```lua
doj = {
    label = 'D.O.J',
    defaultDuty = true,
    offDutyPay = true,
    grades = {
        ['0'] = { name = 'Lawyer', payment = 500 },
        ['1'] = { name = 'Prosecutor', payment = 500 },
        ['2'] = { name = 'Judge', payment = 1000 },
        ['3'] = { name = 'Chief Judge', payment = 1500, isboss = true },
    },
},
```

4. Modify the [config](/scripts/bk_doj/configuration) to your liking and specific resources.
5. Enjoy!
   {% endtab %}

{% tab title="Qbox" %}

1. Place the script in your `resources` folder.
2. Add `ensure bk_doj` to your `server.cfg`. Make sure it starts after the your framework resources.
3. Add the following to `qbx_core/shared/jobs.lua`:

```lua
['doj'] = {
    label = 'D.O.J',
    defaultDuty = true,
    offDutyPay = true,
    grades = {
        [0] = {
            name = 'Lawyer',
            payment = 500
        },
        [1] = {
            name = 'Prosecutor',
            payment = 500
        },
        [2] = {
            name = 'Judge',
            payment = 1000
        },
        [3] = {
            name = 'Chief Judge',
            payment = 1500,
            isboss = true,
            bankAuth = true,
        },
    },
},
```

4. Modify the [config](/scripts/bk_doj/configuration) to your liking and specific resources.
5. Enjoy!
   {% endtab %}
   {% endtabs %}

{% hint style="info" %}
For support,  join our [discord](https://discord.gg/wZryDXaCAU)
{% endhint %}


# Configuration

Available configuration options to customize the script.

```javascript
let Config = {};

Config.Framework = 'auto'; // auto, qbox, qb, esx, custom
Config.Phone = 'auto' // auto, npwd_qbx_mail, qb-phone, lb-phone, gksphone, yseries, jpr-phonesystem, roadphone, custom
Config.Inventory = 'auto' // auto, ox_inventory, qb-inventory, qs-inventory, core_inventory, tgiann-inventory, ps-inventory, custom
Config.Jail = 'auto' // auto, xt-prison, custom

Config.Command = 'doj'; // Command for opening the main UI
Config.Keybind = { // Keymap for opening the UI
    enabled: true,
    key: 'j' // Default key. Can be changed by each player
};

Config.Menus = { // Menu locations
    citizen: {
        coords: [-545.41, -203.76, 37.22], // Coordinates, seperated by a comma
        radius: 2, // The radius from which the menu is accessible
        keybind: 38, // The control ID which should open the menu: https://docs.fivem.net/docs/game-references/controls/#controls,
        ped: {
            enabled: true,
            model: 'a_m_y_business_03',
            heading: 215.0
        }
    },
    lawyer: {
        coords: [-516.57, -210.99, 37.17],
        radius: 2,
        keybind: 38,
        ped: {
            enabled: true,
            model: 'cs_solomon',
            heading: 118.24
        }
    },
};

Config.Job = { // DOJ Job options. Everything here should match your framework job configuration
    name: 'doj',
    grades: [ // All grade levels, starting from 0
        {
            role: 'lawyer',
            isChiefJudge: false // Whether the grade has access to the 'requests' menu and can edit every case. Also has access to framework-specific features like bossmenu.
        },
        {
            role: 'prosecutor',
            isChiefJudge: false // Whether the grade has access to the 'requests' menu and can edit every case. Also has access to framework-specific features like bossmenu.
        },
        {
            role: 'judge',
            isChiefJudge: false // Whether the grade has access to the 'requests' menu and can edit every case. Also has access to framework-specific features like bossmenu.
        },
        {
            role: 'judge',
            isChiefJudge: true // Whether the grade has access to the 'requests' menu and can edit every case. Also has access to framework-specific features like bossmenu.
        },
    ],
}

Config.Fees = {
    fileCase: 100, // Set to 0 to remove fee
    losingParty: 500 // If a criminal case - the prosecutor or convicted. If a civilian case - the losing party.
}

// These are triggered immediately after the case is closed:
Config.AutoFine = true // Automatically fine the defendant of a criminal case if convicted (found guilty).
Config.AutoJail = true // Automatically send the player to jail if found guilty.
Config.AutoTransfer = true // Automatically transfer the settle amount of a civilian case from the losing party to the winning party.

Config.Evidence = {
    slots: 50,
    weight: 100000,
}

// #################################
//                                 #
//         PHONE INTEGRATION       # 
//                                 # 
// #################################

if (Config.Phone === 'auto') Config.Phone = detectPhone();

function detectPhone() {
    if (GetResourceState('qb-phone') === 'started') return 'qb-phone';
    if (GetResourceState('npwd_qbx_mail') === 'started') return 'npwd_qbx_mail';
    if (GetResourceState('lb-phone') === 'started') return 'lb-phone';
    if (GetResourceState('gksphone') === 'started') return 'gksphone';
    if (GetResourceState('yseries') === 'started') return 'yseries';
    if (GetResourceState('jpr-phonesystem') === 'started') return 'jpr-phonesystem';
    if (GetResourceState('roadphone') === 'started') return 'roadphone';
    return 'custom';
}

// These functions are triggered on the server
Config.SendMail = {
    'qb-phone': (cid, sender, subject, message) => {
        exports['qb-phone'].sendNewMailToOffline(cid, { sender, subject, message });
    },
    'npwd_qbx_mail': (cid, sender, subject, message) => {
        TriggerEvent('qb-phone:server:sendNewMailToOffline', cid, { sender, subject, message });
    },
    'lb-phone': (cid, sender, subject, message) => {
        const phoneNumber = exports["lb-phone"].GetEquippedPhoneNumber(cid);
        const email = exports["lb-phone"].GetEmailAddress(phoneNumber);
        exports["lb-phone"].SendMail({
            to: email,
            sender,
            subject,
            message
        })
    },
    'gksphone': (cid, sender, subject, message) => {
        exports["gksphone"].SendNewMailOffline(cid, {
            sender,
            subject,
            message
        });
    },
    'yseries': (cid, sender, subject, message) => {
        const phoneNumber = exports.yseries.GetPhoneNumberByIdentifier(cid);
        exports.yseries.SendMail({
            title: subject,
            sender,
            senderDisplayName: sender,
            content: message,
        }, 'phoneNumber', phoneNumber);
    },
    'jpr-phonesystem': (cid, sender, subject, message) => {
        exports['jpr-phonesystem'].sendNewMailToOffline(cid, {
            sender,
            subject,
            message,
            button: {}
        });
    },
    'roadphone': (cid, sender, subject, message) => {
        exports['roadphone'].sendMailOffline(cid, {
            sender,
            subject,
            message
        });
    },
    'custom': (cid, sender, subject, message) => {},
}

// #################################
//                                 #
//      INVENTORY INTEGRATION      #
//                                 #
// #################################

if (Config.Inventory === 'auto') Config.Inventory = detectInventory();

function detectInventory() {
    if (GetResourceState('ox_inventory') === 'started') return 'ox_inventory';
    if (GetResourceState('qb-inventory') === 'started') return 'qb-inventory';
    if (GetResourceState('qs-inventory') === 'started') return 'qs-inventory';
    if (GetResourceState('core_inventory') === 'started') return 'core_inventory';
    if (GetResourceState('codem-inventory') === 'started') return 'codem-inventory';
    if (GetResourceState('tgiann-inventory') === 'started') return 'tgiann-inventory';
    if (GetResourceState('ps-inventory') === 'started') return 'ps-inventory';
    return 'custom';
}

// These functions are triggered on the server
Config.OpenEvidence = {
    'ox_inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports.ox_inventory.RegisterStash(invId, `D.O.J Evidence - Case #${caseId}`, Config.Evidence.slots, Config.Evidence.weight);
        exports.ox_inventory.forceOpenInventory(src, 'stash', invId);
    },
    'qb-inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports['qb-inventory'].CreateInventory(invId, {
            label: `D.O.J Evidence - Case #${caseId}`,
            maxweight: Config.Evidence.weight,
            slots: Config.Evidence.slots
        });
        exports['qb-inventory'].OpenInventory(src, invId);
    },
    'qs-inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports['qs-inventory'].RegisterStash(src, invId, Config.Evidence.slots, Config.Evidence.weight) 
    },
    'core_inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports.core_inventory.openInventory(src, invId, 'stash', null, null, true, null, false);
    },
    'tgiann-inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports["tgiann-inventory"].RegisterStash(invId, `D.O.J Evidence - Case #${caseId}`, Config.Evidence.slots, Config.Evidence.weight);
        exports["tgiann-inventory"].ForceOpenInventory(src, 'stash', invId);
    },
    'ps-inventory': (src, caseId) => {
        const invId = `doj_evidence:${caseId}`;
        exports['ps-inventory'].OpenInventory('stash', invId, {
            maxweight: Config.Evidence.weight,
            slots: Config.Evidence.slots
        }, src);
    },
}

// #################################
//                                 #
//         JAIL INTEGRATION        #
//                                 #
// #################################

if (Config.Jail === 'auto') Config.Jail = detectJail();

function detectJail() {
    if (GetResourceState('xt-prison') === 'started') return 'xt-prison';
    return 'custom';
}

Config.JailPlayer = {
    'xt-prison': (src, timeInMins) => {
        oxTriggerClientCallback('xt-prison:client:enterJail', src, timeInMins);
    },
    'custom': (src, timeInMins) => {}
}
```

<details>

<summary>Language configuration</summary>

```javascript
let Lang = {};

Lang.CommandSuggestion = 'Open the D.O.J management system';

Lang.Menus = {
    citizen: {
        main: {
            text_ui: '[E] - Open Citizens Menu',
            title: 'Citizens Menu',
            case_details: {
                title: 'View Case Details',
                description: 'Check your own case\'s status or search a public case'
            },
            schedule_appointment: {
                title: 'Schedule a Lawyer Appointment',
                description: 'Send a request for a meeting with your preferred lawyer'
            },
            file_case: {
                title: 'File a New Case',
                description: 'Open a new case against someone bad'
            },
        },
        case_details: {
            title: 'View Case Details',
            my_cases: {
                title: 'My Cases',
                description: 'View details about your current or past cases'
            },
            search_case: {
                title: 'Search Case',
                description: 'View details about a public case by case ID'
            }
        },
        my_cases: {
            title: 'My Cases (%s)',
            format: {
                title: 'Case #%s',
                description: 'Status: %s'
            }
        }
    },
    lawyer: {
        main: {
            text_ui: '[E] - Open Lawyers Menu',
            title: 'Lawyers Menu',
            appointments: {
                title: 'Appointment Requests',
                description: 'View and respond to requests for scheduled appointments.'
            }
        },
        appointments: {
            title: 'Appointment Requests (%s)',
            format: {
                title: '%s',
                description: 'Requested %s'
            }
        },
        appointment: {
            title: 'Appointment Request',
            reason: {
                title: 'Reason for the appointment',
                description: '%s'
            },
            meeting_time: {
                title: 'Preferred meeting time',
                description: '%s'
            },
            respond: {
                title: 'Respond',
                description: 'The person will receive an email and the request will be deleted'
            }
        }
    }
}

Lang.Inputs = {
    search_case: {
        title: 'Search Case',
        case_id_input: {
            title: 'Case ID',
            description: 'Enter the case ID without a #'
        }
    },
    schedule_appointment: {
        title: 'Schedule an Appointment',
        reason: {
            title: 'Reason',
            description: 'Enter the details and the reason for the appointment'
        },
        meeting_date: {
            title: 'Meeting Date',
            description: 'Choose your preferred meeting date',
        },
        meeting_time: {
            title: 'Meeting Time',
            description: 'Enter a convenient time for the appointment'
        },
        lawyer: {
            title: 'Lawyer',
            description: 'Select a lawyer you\'d like to schedule the meeting with'
        }
    },
    file_case: {
        title: 'File A Case',
        case_type: {
            title: 'Case Type',
            description: 'Choose a case type from the ones available',
            criminal: 'Criminal',
            civilian: 'Civilian'
        },
        defendant_cid: {
            title: 'Defendant\'s CID',
            description: 'Enter the other party\'s citizen ID'
        },
        complainant_cid: {
            title: 'Complainant\'s CID',
            description: 'Lawyers only - if you enter a value here, you will be automatically assigned to the case as the complainant\'s lawyer. Leave empty to file a case as a complainant.'
        },
        description: {
            title: 'Description',
            description: 'Describe your case or enter important details'
        }
    },
    respond_appointment: {
        title: 'Respond to an Appointment',
        reply: {
            title: 'Reply',
            description: 'Enter details about the appointment, such as final meeting time'
        }
    }
}

Lang.Errors = {
    operation_failed: 'Operation failed: Internal server error',
    no_data: 'No data returned!',
    invalid_id: 'Invalid Case ID!',
    invalid_fields: 'One or more fields are invalid!',
    invalid_appointment_date: 'You can\'t schedule an appointment in the past!',
    no_permission: 'You are not authorized for this!',
    invalid_cid: {
        basic: 'Citizen ID is invalid or not found!',
        format: '%s citizen ID invalid or not found!',
        defendant: 'Defendant',
        complainant: 'Complainant',
    },
    no_self_case: 'You can\'t open a case against yourself!',
    no_available_lawyers: 'There are no available lawyers at this time.',
    already_scheduled: 'You already have a scheduled appointment with this lawyer!',
    no_self_appointment: 'You can\'t schedule an appointment with yourself!',
    case_not_found: 'Case not found!',
    appointment_not_found: 'Appointment not found!',
    judge_not_found: 'Judge not found!',
    person_already_present: 'The person you\'re trying to add is already part of the case!',
    no_complainant_lawyer: 'You can\'t add a complainant\'s lawyer to this case!',
    person_not_found: 'The person you\'re trying to remove does not exist!',
    verdict_not_found: 'Verdict not found!',
    decision_not_found: 'Decision not found!',
    mails_not_sent: 'Mails could not be sent due to case information not present.'
}

Lang.Messages = {
    case_created: {
        title: 'Successfully filed a new case.',
        description: 'ID: %s'
    },
    appointment_created: {
        title: 'Successfully created an appointment request!',
        description: 'Expect an email response from the lawyer.'
    },
    no_cases: 'You don\'t have any cases.',
    no_appointments: 'You don\'t have any active appointment requests.',
    responded_appointment: 'Successfully responded to the appointment!',
}

Lang.Dialogs = {
    file_case_confirm: {
        title: 'Are you sure?',
        description: 'There is a fee of **$%s** for filing cases.',
        confirm: 'File Case - $%s'
    }
}

Lang.FeeReasons = {
    fileCase: 'Filing a DOJ case',
    losingParty: 'Lost a DOJ case',
    convictionFine: 'Conviction fine',
    settleAmount: 'Settle amount from a DOJ case'
}

Lang.Mails = {
    sender: 'DOJ Delivery System',
    new_appointment: {
        subject: 'New Scheduled Appointment Request From <strong>%s</strong>',
        message: 'You have a new request for an appointment. Here are the details:<br/>Name: <strong>%s</strong><br/>Date & Time: <strong>%s</strong><br/>Reason: <strong>%s</strong><br/>You can respond to the request via the menu.'
    },
    appointment_reply: {
        subject: 'Re: Scheduled appointment request',
        message: 'You are receiving this email because you\'ve recently scheduled an appointment with the lawyer <strong>%s</strong>. Here is their response:<br/><br/><strong>%s</strong><br/><br/>Thank you for using our services.'
    },
    case_created_defendant: {
        subject: 'New case against you has been filed',
        message: 'You are receiving this email because a case against you has been filed. Here are the details:<br/>Case ID: <strong>#%s</strong><br/>Complainant: <strong>%s</strong><br/>Details: <br/><br/><strong>%s</strong><br/><br/>Please note, you will receive an additional email when the case gets approved or rejected. No further action from you is required at this time.',
        prosecutor: '[Prosecutor]'
    },
    case_created_complainant: {
        subject: 'New case by %s has been filed',
        message: 'You are receiving this email because your lawyer has filed a case on your behalf. Here are the details:<br/>Case ID: <strong>#%s</strong><br/>Complainant: <strong>%s</strong><br/>Defendant: <strong>%s</strong><br/>Details: <br/><br/><strong>%s</strong><br/><br/>Please note, you will receive an additional email when the case gets approved or rejected. No further action from you is required at this time.'
    },
    case_approved: {
        subject: 'Case #%s has been approved',
        message: 'You are receiving this email because a case you are a part of has been approved. Expect another email stating the next court session date.'
    },
    case_rejected: {
        subject: 'Case #%s has been rejected',
        message: 'We are informing you that a case you are a part of has been rejected. No further action from you is required.'
    },
    case_assigned: {
        subject: 'You have been assigned to case #%s',
        message: 'Here are the details:<br/>Case ID: <strong>#%s</strong><br/>Type: <strong>%s</strong><br/>Complainant: <strong>%s</strong><br/>Complainant\'s Lawyer: <strong>%s</strong><br/>Defendant: <strong>%s</strong><br/>Details: <br/><br/><strong>%s</strong><br/><br/>You have been assigned by Chief Judge <strong>%s</strong>'
    },
    added_to_case: {
        subject: 'You have been added to case #%s',
        message: 'You are receiving this email because the judge of a case has added you to it.<br/>Your role is: <strong>%s</strong>',
        defendantLawyer: 'Defendant\'s Lawyer',
        complainantLawyer: 'Complainant\'s Lawyer',
        witness: 'Witness'
    },
    next_session_changed: {
        subject: 'Next session for case #%s',
        message: 'The next court session has been scheduled for:<br/><strong>%s</strong><br/>Please make sure you attend the court on this date.'
    },
    next_session_canceled: {
        subject: 'Session for case #%s canceled',
        message: 'The court session that was previously scheduled for <strong>%s</strong> has been canceled. Please expect a new email about the next session. No further action from you is required at this time.'
    }
}
```

</details>


# bk\_banking

Introducing **BK Banking**, a complete financial script built to bring real banking mechanics into your FiveM server. This system delivers a fully functional banking experience with multiple accounts, loan management, debit cards, ATMs, and an advanced bank-inspector interface. Ideal for RP servers seeking a realistic, interactive financial system.

### Features

#### Core features

* Transfer, deposit, and withdraw funds across all account types.
* Comprehensive statistics and detailed transaction logs.
* Fully protected, accurate, and exploit-proof banking logic.

#### Multi-Account System

* **Personal Account:** Standard player account for all personal financial operations.
* **Society/Business Accounts:** Job or organization treasuries, accessible only to authorized leaders.
* **Shared Accounts:** Player-created accounts where you can add other players and assign customizable permissions.

#### Debit Cards & Card Management

* Create multiple cards linked to a single account.
* Freeze, unfreeze, rename, or delete cards.
* Custom PINs with automatic lockout after failed attempts.
* Debit cards exist as physical inventory items for real item handling.

#### Loans

* **Loan Requests:** Players apply directly through the banking UI with chosen amount and duration.
* **Approval Logic:** All loan requests require manual approval from a bank inspector.
* **Inspector Access:** Full loan overview and management for proper oversight.
* **Treasury-Based Funding:** Loans are paid from, and repaid, to the Bank Inspector job treasury.

#### Bank Inspector

A management interface allowing staff to:

* Search accounts by name or IBAN.
* View balances, owners, linked accounts, and activity logs.
* Freeze or unfreeze accounts.
* Review and manage all loan requests directly from the inspector menu, including both pending and active loans. Ensures complete control over server financial activity.

#### Dedicated ATM UI

A streamlined ATM system offering:

* Balance checks and fast cash deposits/withdrawals.
* Secure PIN entry with lockout handling.
* Access exclusively via valid card insertion.

#### Optimized & Configurable

* Supports ESX/QB/Qbox/Custom frameworks.
* Fully optimized with detailed Discord webhooks.
* Highly configurable to match any server’s needs.

The script is protected with [FiveM Asset Escrow system](https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151)

<figure><img src="/files/OKNk2bt6DR1AdRRSekIC" alt=""><figcaption></figcaption></figure>


# Dependencies

A list of required resources to run BK Banking.

<details>

<summary>Supported Frameworks</summary>

**Supported frameworks:**

* `qbox`
* `qb` (QBCore)
* `esx`
* `custom`

</details>

<details>

<summary><a href="https://github.com/CommunityOx/oxmysql">oxmysql</a></summary>

A high-performance MySQL wrapper for FiveM resources, allowing scripts to efficiently interact with MySQL databases.

</details>

<details>

<summary><a href="https://github.com/CommunityOx/ox_lib">ox_lib</a></summary>

Used for the menus, notifications and the input options in the script.

</details>

<details>

<summary>Inventory (recommended for better experience, not required)</summary>

Connects to your inventory system for giving bank cards, with automatic detection or custom configuration.

**Supported inventory systems:**

* ​[`ox_inventory`](https://github.com/CommunityOx/ox_inventory)​
* ​[`qb-inventory`](https://github.com/qbcore-framework/qb-inventory)​
* `custom`

</details>

{% hint style="success" %}
If you need assistance setting up custom scripts alongside our resources, you can open a ticket in our [Discord](https://discord.gg/wZryDXaCAU). While we’ll do our best to help, please note that we can’t guarantee full compatibility if the custom script has issues outside of our control.
{% endhint %}

{% hint style="warning" %}
There’s a chance some of these scripts might already be installed on your server. Installing duplicates can cause conflicts or errors, so always check your existing resources before adding new ones.
{% endhint %}


# Installation

Step-by-step guide to adding the BK Banking to your server.

1. Place the script in your `resources` folder.
2. Make sure that you have all [dependencies](/scripts/bk_banking/dependencies) installed.
3. Add `ensure bk_banking` to your `server.cfg`. Make sure it starts after the framework resources.
4. Modify the config to your liking and specific resources (don't forget to configure the Inspector job).
5. Configure inventory items:

<details>

<summary>If you use ox_inventory</summary>

Go to ox\_inventory\data\items.lua and paste the following code:

```lua
	['debitcard_personal'] = {
		label = 'Personal Debit Card',
		weight = 80,
		client = {
		export = 'bk_banking.UseCardOnAtm'
		},
		},
	['debitcard_shared'] = {
		label = 'Shared Debit Card',
		weight = 80,
		client = {
		export = 'bk_banking.UseCardOnAtm'
		},
	},
	['debit_card'] = {
		label = 'Debit Card',
		weight = 80,
		client = {
		export = 'bk_banking.UseCardOnAtm'
		},
	},
```

</details>

<details>

<summary>if you use qb_inventory</summary>

Go to qb-core\shared\items.lua and paste the following code:

```lua
    debitcard_personal           = { name = "debitcard_personal", label = "Personal Debit Card", weight = 100, type = "item", image = "", unique = true, useable = true, shouldClose = true, combinable = nil, description = "Personal Debit Card" },
    debitcard_business           = { name = "debitcard_business", label = "Business Debit Card", weight = 100, type = "item", image = "", unique = true, useable = true, shouldClose = true, combinable = nil, description = "Business Debit Card" },
    debitcard_shared             = { name = "debitcard_shared", label = "Shared Debit Card", weight = 100, type = "item", image = "", unique = true, useable = true, shouldClose = true, combinable = nil, description = "Shared Debit Card" },
```

</details>

6. Enjoy!

{% hint style="info" %}
For support,  join our [discord](https://discord.gg/wZryDXaCAU)
{% endhint %}


# Configuration

Available configuration options to customize the script.

{% tabs %}
{% tab title="Main configuration" %}
{% code title="config.lua" %}

```lua
Config = {}

Config.Debug = false

-- Framework Configuration
Config.Framework = 'auto' -- auto | qbox | qb | esx | standalone
--[[
    Supported frameworks:
        * esx: es_extended, https://github.com/esx-framework/esx-legacy
        * qbox: qbx_core, https://github.com/qbox-project/qbx_core
        * qb: qb-core, https://github.com/qbcore-framework/qb-core
        * standalone: no framework, note that framework specific apps will not work unless you implement the functions in `standalone.lua`. - vMenu basic support included.
--]]

-- Interaction Configuration
Config.InteractOption = 'drawtext' -- ox_target, qb-target, interact, sleepless_interact, drawtext

-- Inventory Configuration
Config.Inventory = 'auto' -- ox_inventory, qb-inventory, custom

-- Bank Locations
Config.BankLocations = {
  [1] = { -- Pacific Standard
    model = 'u_m_m_bankman',
    coords = vector4(241.44, 227.19, 106.29, 170.43),
    bankName = 'pacific'
  },
  [2] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(310.21, -282.93, 54.16, 335.72),
    bankName = 'fleeca'
  },
  [3] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(145.88, -1044.56, 29.37, 337.36),
    bankName = 'fleeca'
  },
  [4] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(-354.88, -53.79, 49.04, 336.97),
    bankName = 'fleeca'
  },
  [5] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(-1212.58, -336.27, 37.78, 21.89),
    bankName = 'fleeca'
  },
  [6] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(-2957.84, 480.34, 15.70, 87.51),
    bankName = 'fleeca'
  },
  [7] = { -- Fleeca Bank
    model = 'ig_barry',
    coords = vector4(1177.49, 2711.78, 38.09, 176.80),
    bankName = 'fleeca'
  },
  [8] = { -- Blaine County Savings(Paleto)
    model = 'u_m_m_bankman',
    coords = vector4(-112.22, 6471.01, 31.63, 134.18),
    bankName = 'blaine'
  }
}

-- ATM Models
Config.Atms = {
  `prop_atm_01`,
  `prop_atm_02`,
  `prop_atm_03`,
  `prop_fleeca_atm`
}

-- Loans Configuration
Config.Loans = {
  interestPercent = 5 -- default annual/simple rate used for loan applications
}

-- Bank Inspector Job Configuration
Config.BankInspector = {
  enabled = true,          -- Enable/disable bank inspector system
  jobName = 'police',        -- Job name that can access inspector (e.g., 'bank')
  minGrade = 0,            -- Minimum grade required to access inspector (0 = any grade)
  -- When approving a loan, funds will be deducted from this job's business account
  useSocietyAccount = true -- Requires a business account for the job (auto-created on startup)
}
```

{% endcode %}
{% endtab %}

{% tab title="Cards config" %}
{% code title="config.cards.lua" %}

```lua
-- Card Configuration
-- BK Banking System

Config = Config or {}

-- Card Creation Limits
Config.LimitCardCreation = {
    enabled = true,
    maxCardsPerAccount = 10, -- Maximum cards per account
}

-- Card Item Requirements
Config.CardItems = {
    "debitcard_personal", -- Item name for bank card
    "debitcard_shared",   -- Item name for credit card
    "debitcard_personal"          -- Item name for debit card
}


-- Card Types Configuration
Config.CardTypes = {
    debit = {
        name = "Debit Card",
        givesItem = true,
        itemName = "debitcard_personal"
    },
    credit = {
        name = "Credit Card",
        givesItem = true,
        itemName = "credit_card"
    },
    business = {
        name = "Business Card",
        givesItem = true,
        itemName = "bank_card"
    }
}

-- Card Generation Settings
Config.CardGeneration = {
    cardNumberLength = 16, -- Length of generated card numbers
    expirationYears = 5    -- Years until card expiration
}

-- Card Security Settings
Config.CardSecurity = {
    allowPinChange = true,            -- Allow PIN changes
    maxPinAttempts = 3,               -- Maximum PIN attempts before lockout
    lockoutDuration = 300,            -- Lockout duration in seconds
    requirePinForTransactions = false -- Require PIN for transactions
}
```

{% endcode %}
{% endtab %}

{% tab title="Accounts config" %}
{% code title="config.accounts.lua" %}

```lua
-- Account Configuration
-- BK Banking System

Config = Config or {}

-- Account Creation Limits
Config.LimitAccountCreation = {
    enabled = true,
    maxSharedAccounts = 3    -- Maximum shared accounts per player
}
```

{% endcode %}
{% endtab %}

{% tab title="Logs config" %}
{% code title="logs.lua" %}

````lua
-- Discord Webhook Logging System for bk_banking

-- Discord Webhook Configuration
local DiscordConfig = {
    enabled = true,                                                                                                                           -- Enable/disable Discord webhook logging
    webhookUrl =
    "",                                                                                                                                       -- Discord webhook URL
    avatarUrl = '',                                                                                                                           -- Avatar URL for Discord messages
    username = 'BK Banking Logs',                                                                                                             -- Username for Discord messages
    color = 0x128b7d,                                                                                                                         -- Embed color (hex)
    timeout = 10,                                                                                                                             -- Timeout in seconds before sending queued logs
    batchSize = 10,                                                                                                                           -- Number of logs to batch before sending
    excludedLogTypes = {                                                                                                                      -- Log types to exclude from Discord
        'account_member_modified',
        'account_name_updated',
        'card_limits_updated',
        'card_name_updated',
    }
}

-- List of all logTypes for filtering
-- 'withdraw', 'deposit', 'transfer', 'card_created', 'card_item_given',
-- 'card_frozen_due_to_too_many_attempts', 'pin_change_failed', 'pin_changed_successfully',
-- 'card_name_updated', 'card_limits_updated', 'card_unfrozen', 'card_frozen', 'card_terminated',
-- 'account_created', 'account_freeze_failed', 'account_unfreeze_failed', 'account_terminate_failed',
-- 'account_freeze', 'account_unfreeze', 'account_terminate', 'account_member_added',
-- 'account_member_removed', 'account_name_updated', 'account_member_modified',
-- 'loan_applied', 'loan_approved', 'loan_rejected', 'loan_payment_made'

local logQueue = {}

RegisterNetEvent('bk_banking:server:logs:create', function(iban, playerId, logType, logMessage)
    local postData = {}

    -- Check if Discord logging is enabled
    if not DiscordConfig.enabled then
        return
    end

    local webhook = DiscordConfig.webhookUrl
    if not webhook or webhook == "SET_YOUR_WEBHOOK_URL_IN_SERVER.CFG" then
        print('^3[bk_banking]^7 Tried to post a log but webhook is not configured')
        return
    end

    -- Check if the logType is in the excluded list
    for i = 1, #DiscordConfig.excludedLogTypes do
        if DiscordConfig.excludedLogTypes[i] == logType then
            return
        end
    end

    -- Create an embed
    local embed = {
        ['type'] = 'rich',
        ['color'] = DiscordConfig.color,
        ['footer'] = {
            ['text'] = os.date('%c'),
        },
        ['author'] = {
            ['name'] = DiscordConfig.username,
            ['icon_url'] = DiscordConfig.avatarUrl,
        },
        ['fields'] = {
            {
                ['name'] = 'Player Identifier / IBAN / Log Type',
                ['value'] = "```" .. playerId .. " / " .. iban .. " / " .. logType .. "```",
            },
            {
                ['name'] = 'Log Message:',
                ['value'] = "```" .. logMessage .. "```",
            },
        }
    }

    if not logQueue[iban] then logQueue[iban] = {} end
    logQueue[iban][#logQueue[iban] + 1] = { webhook = webhook, data = embed }

    -- Process the logQueue when it reaches a certain size or based on other criteria
    if #logQueue[iban] >= DiscordConfig.batchSize then
        postData = {
            username = DiscordConfig.username,
            avatar_url = DiscordConfig.avatarUrl,
            embeds = {},
        }

        for i = 1, #logQueue[iban] do
            postData.embeds[#postData.embeds + 1] = logQueue[iban][i].data
        end

        PerformHttpRequest(webhook, function() end, 'POST', json.encode(postData),
            { ['Content-Type'] = 'application/json' })
        logQueue[iban] = {}
    end
end)

CreateThread(function()
    local timer = 0
    while true do
        Wait(5000)

        timer = timer + 5
        if timer >= DiscordConfig.timeout then -- If timeout seconds have passed, post the logs.
            timer = 0
            for iban, queue in pairs(logQueue) do
                if #queue > 0 then
                    local postData = {
                        username = DiscordConfig.username,
                        avatar_url = DiscordConfig.avatarUrl,
                        embeds = {}
                    }

                    for i = 1, #queue do
                        -- Directly add the embed from each log entry in the queue
                        postData.embeds[#postData.embeds + 1] = queue[i].data
                    end

                    -- Send the logs to Discord
                    PerformHttpRequest(queue[1].webhook, function() end, 'POST', json.encode(postData),
                        { ['Content-Type'] = 'application/json' })

                    -- Clear the queue for this iban after sending
                    logQueue[iban] = {}
                end
            end
        end
    end
end)

````

{% endcode %}
{% endtab %}
{% endtabs %}


# Exports

Available exports for integrating the BK Banking with other scripts.

**WIP**


# bk\_garages

BK Garages is an advanced garage system built around a unique concept: **allowing players to park their vehicles anywhere in the open world**. Parked vehicles are replaced with optimized **solid object models** (SOM) that maintain visual presence without using the resources of real vehicle entities, significantly reducing server load. With its modular design, the resource is highly configurable, making it suitable for any server and community, while being built with modern development techniques to ensure a clean, scalable, and performance-focused solution that remains stable even on large servers. The system also follows strict security practices, ensuring reliable and exploit-resistant behavior.

### Features

#### Garage System

**Outside Garages:** Allows players to park their vehicles anywhere on the map instead of being limited to fixed garage locations. When a vehicle is parked, it is replaced with an optimized visual model (SOM) that looks like a real parked car but does not use the same resources as an actual vehicle entity. This keeps the world feeling alive while avoiding unnecessary server load and removes the need for traditional garages.&#x20;

**Inside Garages:** Store vehicles in traditional location-based garages, giving servers a familiar and reliable option for more classic vehicle storage.&#x20;

**Private Garages:** Create exclusive garages for houses, businesses, or custom-owned properties, adding more value and immersion to personal or group ownership.&#x20;

**Depot System:** Recover missing, relocated, or unavailable vehicles through a configurable depot.

**Impound System:** Manage confiscated vehicles through dedicated impound lots and police workflows, where vehicles are physically stored and take up parking spaces for added realism and immersion.

#### Vehicle Management

**Owned Vehicle Highlighting:** Easily identify vehicles that belong to you, making it much easier to spot your own vehicle in busy areas or large parking spaces.&#x20;

**Garage Menu:** View, search, and manage all owned vehicles from one intuitive interface.&#x20;

**Parking Animations:** Adds extra immersion when storing vehicles.&#x20;

**Blacklist Support:** Restrict specific vehicle models or categories when needed.

#### Gameplay Features

**Safezones:** Set protected zones around garages and important areas, helping reduce griefing and keeping high-traffic locations better controlled.&#x20;

**Car Jacker:** Adds support for vehicle theft roleplay scenarios, allowing players to break into vehicles and even disable the alarm system for a more immersive and controlled stealing experience.

#### Performance, Security & Compatibility

**Cache System:** Improves responsiveness and reduces unnecessary processing.&#x20;

**Security Features:** Built with secure handling and controlled logic in mind.&#x20;

**Fully Optimized:** Developed with performance-first architecture for stable use on busy servers.&#x20;

**Multiple Framework Support:** Easy to integrate into a wide variety of server setups.

Don't miss out - transform your roleplay experience today by switching to our system that solves one of the most overlooked problems in FiveM - where you store your vehicles.

The script is protected with [FiveM Asset Escrow system](https://forum.cfx.re/t/introducing-asset-escrow-for-your-resources/4777151).

<figure><img src="/files/0WyrDYmvHm4sJ5u53UhA" alt=""><figcaption></figcaption></figure>


# Dependencies

A list of required resources to run BK Garages.

<details>

<summary>Supported Frameworks</summary>

**Supported frameworks:**

* `qbox`
* `qb` (QBCore)  version 1.3.0+
* `esx`
* `custom`

</details>

<details>

<summary><a href="https://mariadb.org/download/">MariaDB</a> (recommended) or any other SQL database</summary>

An open-source database, compatible with MySQL, used in FiveM to store and manage persistent server data like player info, vehicles, and economy records.

</details>

<details>

<summary><a href="https://github.com/CommunityOx/oxmysql">oxmysql</a></summary>

A high-performance MySQL wrapper for FiveM resources, allowing scripts to efficiently interact with MySQL databases.

</details>

<details>

<summary><a href="https://github.com/CommunityOx/ox_lib">ox_lib</a></summary>

Used for the menus, notifications and the input options in the script.

</details>

<details>

<summary><a href="https://github.com/mkafrin/PolyZone/releases/latest">PolyZone</a></summary>

Required for handling garage areas and interaction zones across the map.

</details>

<details>

<summary><a href="https://github.com/GttMone/ps-zones">ps-zones</a></summary>

IMPORTANT: Download the modified version otherwise the resource will not work! - <https://github.com/GttMone/ps-zones>

</details>

<details>

<summary><a href="https://github.com/Gl1tchStudios/glitch-minigames">glitch-minigames </a>(Optional)</summary>

Required only if you want to use the carjacker item.

</details>

<details>

<summary><a href="https://github.com/communityox/ox_target/releases/latest">ox_target</a> (Optional)</summary>

Enhances interaction with a clean, target-based system for a smoother user experience.

</details>

{% hint style="success" %}
If you need assistance setting up custom scripts alongside our resources, you can open a ticket in our [Discord](https://discord.gg/wZryDXaCAU). While we’ll do our best to help, please note that we can’t guarantee full compatibility if the custom script has issues outside of our control.
{% endhint %}

{% hint style="warning" %}
There’s a chance some of these scripts might already be installed on your server. Installing duplicates can cause conflicts or errors, so always check your existing resources before adding new ones.
{% endhint %}


# Installation

Step-by-step guide to adding the BK Garages to your server.

1. Install all [dependencies](/scripts/bk_garages/dependencies) and make sure to ensure them in the `server.cfg`.
2. Place the bk\_garages folder somewhere in your resources directory.
3. Ensure the resource in your `server.cfg` as follows, **AFTER** all dependencies and core/framework resources:

```
ensure bk_garages
```

4. Modify the configuration files in the `/config` folder to your liking.
5. If you want to use the carjacker item (recommended), register the item in your inventory items file. Below are some examples:

<details>

<summary>If you use ox_inventory</summary>

* ox\_inventory/data/shared/items.lua:

```
['carjacker'] = {
    label = 'Car Jacker',
    weight = 2000,
    stack = false,
    consume = 0,
    description = 'Hmm, what could that do to parked cars...',
    server = {
        export = 'bk_garages.carJackerUsed'
    }
},
```

</details>

<details>

<summary>if you use qb_inventory</summary>

* qb-core/shared/items.lua

```
carjacker = { name = 'carjacker', label = 'Car Jacker', weight = 2000, type = 'item', image = 'carjacker.png', unique = true, useable = true, shouldClose = true, description = 'Hmm, what could that do to parked cars...' },
```

</details>

6. Add the item image to your inventory icons folder.
7. Enjoy!

{% hint style="info" %}
For support,  join our [discord](https://discord.gg/wZryDXaCAU)
{% endhint %}


# Configuration

Available configuration options to customize the script.

{% tabs %}
{% tab title="config.js" %}

```javascript
const Config = {};

// Legend
// Outside Garages = Refers to parking the vehicle anywhere on the map and a prop model appearing in it's place.
// Inside Garages = Refers to parking the vehicle in a specific garage and despawning it.

// ⚠⚠⚠ = An option that could cause the resource to stop working as intended or cause performance issues. Use with caution!
// DISCLAIMER: NO SUPPORT WILL BE PROVIDED IF ANY OF THE OPTIONS MARKED WITH ⚠⚠⚠ ARE NOT THE DEFAULT VALUES!

// ####################################
// #                                  #
// #                                  #
// #         GLOBAL OPTIONS           #
// #                                  #
// #                                  #
// ####################################

Config.Framework = 'auto'; // auto, qbox, qb, esx, custom
// The default framework inventory is supported out of the box.
// If you use another inventory, modify the code in dist/bridge/server/(your_framework).js

Config.Debug = false // Prints additional debug messages

Config.GarageMenu = {
    enabled: true,
    command: {
        name: 'garage',
        description: 'View and manage all your vehicles'
    },
    remoteUnpark: { // Allows players to remotely unpark/spawn their outside vehicles from the menu
        enabled: true,
        giveKeyRadius: 10 // Shows all players in range of the vehicle that should receive the keys
    },
    markLocation: { // Ability for player to mark the vehicle location when parked or impounded
        inside: true,
        outside: true,
        impound: false
    }
};

Config.Interactions = {
    maxDistance: 1.7,
    canParkWhileInside: true, // Whether you can park the vehicle while sitting in it.
    canParkWithPassengers: true, // Whether you can park the vehicle if it has passengers inside.
    keybind: 38 // Used for TextUI interactions. https://docs.fivem.net/docs/game-references/controls/#controls
};

Config.Permissions = {
    canParkIfNotOwned: { // Whether anyone can park a vehicle, even if it's owned by someone else.
        outside: true,
        inside: true
    },
    canParkIfNoAccess: { // Whether anyone can park a vehicle inside a garage they do not have access to.
        inside: true
    },
};

Config.Animations = {
    park: { // Vehicle fading when parking
        outside: true,
        inside: true,
        rate: 3, // The rate at which the animation is played
        delay: 1 // The delay in miliseconds between the next animation frame
    },
    appear: { // Object appearing after vehicle has been parked
        outside: true,
        rate: 3, // The rate at which the animation is played
        delay: 1 // The delay in miliseconds between the next animation frame
    },
};

Config.SpawnOptions = {
    outside: {
        unparkLockState: 1, // 0 = default game state; 1 = unlocked; 2 = locked; This is after the vehicle has been spawned (unparked).
        engineRunning: false,
        warpIntoVehicle: false,
    },
    inside: {
        unparkLockState: 1, // 0 = default game state; 1 = unlocked; 2 = locked; This is after the vehicle has been spawned (unparked).
        engineRunning: true,
        warpIntoVehicle: true,
        checkOccupied: { // Check if the area is occupied by another vehicle when taking out of garage
            enabled: true,
            distance: 2
        },
    },
    depot: {
        unparkLockState: 1, // 0 = default game state; 1 = unlocked; 2 = locked; This is after the vehicle has been spawned.
        engineRunning: true,
        warpIntoVehicle: true,
        checkOccupied: { // Check if the area is occupied by another vehicle when taking out of garage
            enabled: true,
            distance: 2
        },
    },
    jacked: { // When the vehicle has been carjacked
        unparkLockState: 2, // 0 = default game state; 1 = unlocked; 2 = locked; This is after the vehicle has been spawned (unparked).
        engineRunning: false,
        warpIntoVehicle: false,
    },
    impound: {
        unparkLockState: 2,
        engineRunning: false,
        warpIntoVehicle: false,
        checkOccupied: { // Check if the area is occupied by another vehicle when SPAWNING non-physical vehicle from impound
            enabled: true,
            distance: 2
        },
    }
};


// When spawning a vehicle through the depot, it will be repaired with full health.
// Depending on the current state of the vehicle, the price is different. Prices can be changed below.
// When spawning via the depot, if the old vehicle exists on the map it will be deleted to prevent duplication.
Config.Depot = {
    interactions: {
        preferTarget: true, // If true, will use ox_target if available, false for text UI
        targetOptions: {
            name: 'Open Depot',
            icon: 'warehouse'
        },
    },
    prices: { // Prices depend on the vehicle state when taking out of the depot.
        missingWorking: 1500, // Vehicle is not spawned on the map and when it was last spawned it's engine was working.
        missingBroken: 10000, // Vehicle is not spawned on the map and when it was last spawned, it's engine was broken.
        spawnedBroken: 3000, // Vehicle is spawned on the map, but it's engine is broken.
        spawnedWorking: 7000 // Vehicle is spawned and it's not broken.
    },
    locations: {
        city: {
            label: 'Los Santos',
            type: 'land', // land, water, air
            points: {
                menu: [-160.6362, -1162.0336, 22.8887, 269.8439],
                spawn: [
                    [-151.9821, -1162.2888, 25.2869, 259.7493],
                    [-152.1170, -1165.7527, 25.2589, 269.0813],
                    [-152.1190, -1168.9435, 25.2176, 267.7451],
                    [-152.2957, -1172.0688, 25.1755, 265.3527],
                    [-152.5797, -1175.0745, 25.1296, 268.2219]
                ]
            },
            blip: {
                enabled: true,
                sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
                color: 1, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
                scale: 0.7,
            },
            npcModel: 's_m_m_ccrew_01'
        },
        paleto: {
            label: 'Paleto Bay',
            type: 'land', // land, water, air
            points: {
                menu: [96.0323, 6363.4253, 30.3759, 10.9867],
                spawn: [
                    [95.0378, 6372.0400, 31.2258, 9.3269],
                    [98.6111, 6373.8335, 31.2258, 7.9667],
                    [101.9770, 6376.3726, 31.2258, 8.3493],
                ]
            },
            blip: {
                enabled: true,
                sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
                color: 1, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
                scale: 0.7,
            },
            npcModel: 's_m_m_ccrew_01'
        },
        sandy: {
            label: 'Sandy Shores',
            type: 'land', // land, water, air
            points: {
                menu: [1898.4939, 3707.3521, 31.7805, 117.1301],
                spawn: [
                    [1894.8556, 3706.2773, 32.8254, 118.1982],
                ]
            },
            blip: {
                enabled: true,
                sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
                color: 1, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
                scale: 0.7,
            },
            npcModel: 's_m_m_ccrew_01'
        },
        airport: {
            label: 'City Airport',
            type: 'air', // land, water, air
            points: {
                menu: [-1289.3459, -3350.7886, 12.9401, 239.8171],
                spawn: [
                    [-1274.1547, -3379.7485, 13.9401, 327.4670],
                ]
            },
            blip: {
                enabled: true,
                sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
                color: 1, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
                scale: 0.7,
            },
            npcModel: 's_m_m_ccrew_01'
        },
        docks: {
            label: 'City Docks',
            type: 'water', // land, water, air
            points: {
                menu: [611.4539, -3204.2271, 5.0695, 0.9396],
                spawn: [
                    [621.7027, -3238.6921, 13.1654, 181.7283],
                ]
            },
            blip: {
                enabled: true,
                sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
                color: 1, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
                scale: 0.7,
            },
            npcModel: 's_m_m_ccrew_01'
        },
    }
};


Config.Impound = {
    command: {
        name: 'confiscate',
        description: 'Impound a parked or spawned vehicle (police only)',
        params: ['Name of the impound (optional)', 'Slot where the vehicle should be placed, starting from 0 (optional)'],
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
    },
    depotCommand: {
        name: 'depotveh',
        description: 'Remove a spawned vehicle and send it depot. (police only)',
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
    },
    interactions: {
        menu: { // Impound menu
            preferTarget: true, // If true, will use ox_target if available, false for text UI
            targetOptions: {
                name: 'Open Impound',
                icon: 'warehouse'
            },
        },
        release: { // Target for physical vehicles
            enabled: true,
            targetOptions: {
                name: 'Release Vehicle',
                icon: 'car-on'
            },
        }
    },
    transferFee: 2500, // Fee for transferring a vehicle from one impound to another
    giveKeyRadius: 10, // Displays all nearby players with this radius to receive the vehicle key when releasing.
    purge: { // An option to remove all vehicles from an impound. Action is defined below
        enabled: true,
        accessGroups: { police: 4 }, // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.

        // despawn = keep in current depot, but non-physical. depot = send to depot. delete = permanently delete vehicle from garage
        // NOTE: it will only delete vehicles from the garage table, NOT the framework's player vehicles table. Meaning, they can be spawned again, just
        // not with up-to-date information.
        action: 'depot' // 'despawn' | 'depot' | 'delete'
    },
    // When the impound is full and a new vehicle is impounded, what should happen to the first impounded vehicle (oldest).
    // deny = cannot impound new vehicles; despawn = remove first vehicle as physical but keep in impound; depot = send to depot; delete = permanently delete from garages DB
    overflowAction: 'deny', // 'deny' | 'despawn' | 'depot' | 'delete'
    locations: {
        // ##################################################################
        // #                                                                #
        // #   ALL IMPOUND LOCATIONS CAN BE FOUND IN THE ./impounds.js FILE #
        // #                                                                #
        // ##################################################################
    }
};

// ###########################################################################################################################################
// ###########################################################################################################################################


// ####################################
// #                                  #
// #                                  #
// #         OUTSIDE GARAGES          #
// #                                  #
// #                                  #
// ####################################

// The chunks determine which vehicles are loaded at the same time. They should cover the whole map.
// ATTENTION: AFTER ADJUSTING VALUES BELOW, ALL VEHICLES THAT ARE CURRENTLY PARKED OUTSIDE AND DO NOT BELONG IN THE NEW CHUNK WILL BE PUT IN THE DEPOT!
Config.Chunks = { // ⚠⚠⚠ CAUTION ⚠⚠⚠

    // These are the coordinates of the 4 corners of the map plus min and max height. 
    // If you have a map extension (like the cayo perico), you should adjust so the chunks cover that additional area.
    boundaries: {
        MAP_MIN_X: -4000,
        MAP_MAX_X: 4500,
        MAP_MIN_Y: -4000,
        MAP_MAX_Y: 8000,
        MAP_MIN_Z: -150,
        MAP_MAX_Z: 1000
    },
    CHUNK_SIZE: 200, // This determines the chunk size. The greater the value, the more stored vehicles will be loaded at a time. DISCLAIMER: TOO SMALL OF A CHUNK SIZE CAN CAUSE PERFORMANCE ISSUES!
    debug: false // Set to true to visualize the chunks (WARNING: Drastically decreases performance)
}

Config.Interactions.Outside = {
    enabled: true,
    park: {
        command: {
            enabled: true,
            name: 'park',
            description: 'Park the closest vehicle or the one you\'re in'
        },
        radial: {
            enabled: true,
            name: 'Park Vehicle',
            icon: 'square-parking'
        },
        target: {
            enabled: true,
            name: 'Park Vehicle',
            icon: 'square-parking'
        },
    },
    unpark: {
        command: {
            enabled: true,
            name: 'unpark',
            description: 'Unpark the closest vehicle'
        },
        radial: {
            enabled: true,
            name: 'Unpark Vehicle',
            icon: 'warehouse'
        },
        target: {
            enabled: true,
            name: 'Unpark Vehicle',
            icon: 'warehouse'
        }
    },
}

// An item that is used for 'jacking' (unparking) outside parked vehicles.
Config.CarJacker = {
    enabled: true,
    item: 'carjacker',
    removeOnFail: true, // Whether the item should be removed from the player if they've failed the minigame.
    removeOnSuccess: true, // Whether the item should be removed from the player after they've succeeded the minigame.
    startMinigame: () => { // This is ran on the client and must return a boolean whether the minigame is successfull.
        return exports['glitch-minigames'].StartFirewallPulse();
    },
    alarm: {
        enabled: true,
        timeout: 60, // After how many seconds the alarm stops if the minigame was failed
        requireMinigame: true, // If true, it will require a second minigame in order to stop the alarm. If false, the alarm stops as soon as the vehicle is jacked.
        startMinigame: () => {
            return exports['glitch-minigames'].StartCircuitBreaker(Math.floor(Math.random() * 2) + 1, 0, 2000, 500, 500, 0, 0);
        },
    },
    giveKeys: false // Whether the player who jacked the vehicle should receive the keys or have to lockpick it.
}

Config.VehicleClasses = [ // What object models should appear when the vehicle is parked, depending on it's class. (https://docs.fivem.net/natives/?_0x29439776AAA00A62)
    'imp_prop_covered_vehicle_02a', // Compacts
    'imp_prop_covered_vehicle_01a', // Sedans
    'imp_prop_covered_vehicle_07a', // SUVs
    'imp_prop_covered_vehicle_03a', // Coupes
    'imp_prop_covered_vehicle_03a', // Muscle
    'imp_prop_covered_vehicle_04a', // Sports Classic
    'imp_prop_covered_vehicle_01a', // Sports
    'imp_prop_covered_vehicle_01a', //Super
    'prop_rub_bike_03', // Motorcycles
    'imp_prop_covered_vehicle_07a', // Offroad
    'prop_flattrailer_01a', // Industrial
    'imp_prop_covered_vehicle_05a', // Utility
    'imp_prop_covered_vehicle_05a', // Vans
    'prop_rub_bike_02', // Cycles
    'prop_byard_boat01', // Boats
    'prop_container_03a', // Helicopters
    'hei_prop_carrier_jet', // Planes
    'imp_prop_covered_vehicle_05a', // Service
    'imp_prop_covered_vehicle_03a', // Emergency
    'prop_container_01e', // Military
    'prop_truktrailer_01a', // Commercial
    '', // Trains
    '' // Open Wheel
];

Config.VehicleModels = { // This overrides the class-based mapping and for each individual vehicle model, a parked object model can be chosen. 
    // 'vehicleModel': 'parkedModel', // Remove the '//' at the start of the line
    // 'vehicleModel': 'parkedModel',
    // 'vehicleModel': 'parkedModel',
};

Config.BlacklistedTypes = { // Prevent vehicles with this type from being parked outside.
    automobile: false, // Set to true to prevent it from being parked.
    bike: false,
    boat: false,
    heli: false,
    plane: false,
    submarine: false,
    trailer: false,
    train: false
};

Config.BlacklistedModels = [ // Prevent specific vehicle models from being parked outside.
    'kosatka',
];

Config.GroundCheck = { // Checks whether the vehicle is on all 4 wheels before parking it.
    automobile: true,
    bike: true,
    boat: true,
    heli: true,
    plane: true,
    submarine: true,
    trailer: true,
    train: true
};

Config.HighlightOwnedVehicles = { // Whether to highlight parked vehicles when the player gets near them if they own them
    enabled: true,
    options: {
        distance: 5,
        color: [255, 255, 255, 255], // RGBA
        glow: true
    },
    interactions: {
        command: {
            enabled: true,
            name: 'highlight',
            description: 'Toggle highlight for nearby parked vehicles that you own'
        },
        keybind: {
            enabled: true,
            default: 'LBRACKET', // Default keybind for new players. Can be changed by each user individually.
            toggle: false, // true = toggleable, false = hold key
            description: 'Highlight owned parked vehicles'
        }
    },
    default: false // Whether the highlight is enabled/disabled by default. If no interactions are enabled, set this to 'true'
};

// Inside garage store points and impound zones are automatically added. Only depot spawn points need to be manually added.
Config.SafeZones = {
    pointsRadius: 5, // Radius of safe zones created around Inside Garage interaction points (park, spawn, menu).
    zones: [ // Make sure each zone is large enough so players can't park a vehicle in the zone while they're outside the zone. (Config.Interactions.maxDistance)
        
        // City Depot
        {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(-161.07232666016, -1158.0786132812),
                vector2(-161.16448974609, -1186.2835693359),
                vector2(-140.00260925293, -1186.2198486328),
                vector2(-140.75495910645, -1157.6209716797)
            ],
            minZ: 22,
            maxZ: 28
        },

        // Paleto Depot
        {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(86.633056640625, 6377.8676757812),
                vector2(94.066596984863, 6361.9736328125),
                vector2(115.9801864624, 6372.9194335938),
                vector2(105.732421875, 6390.16015625)
            ],
            minZ: 30,
            maxZ: 33
        },

        // Sandy Depot
        {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(1903.8697509766, 3702.1652832031),
                vector2(1893.3161621094, 3695.7080078125),
                vector2(1884.6793212891, 3706.6765136719),
                vector2(1895.2440185547, 3714.1262207031)
            ],
            minZ: 31,
            maxZ: 35
        },
    ],
    debug: false // Whether to visualize the polyzones
};

Config.Blips = {
    enabled: true, // Enables blips on the map for parked vehicles outside of garages.
    color: 0xe9a7fffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
    scale: 0.8,
    keepOnMinimap: true, // Whether the blips should always appear on the minimap, even if not near them.
    limit: 10, // If keepOnMinimap is true, shows only the last x parked vehicles after joining (Can increase after parking new vehicles). Set to <0 to show all.
    shrink: true, // If keepOnMinimap is true, shrinks the blips on the minimap so they're not in the way
    sprites: { // https://docs.fivem.net/docs/game-references/blips/
        automobile: 825,
        bike: 749,
        boat: 455,
        heli: 753,
        plane: 579,
        submarine: 308,
        trailer: 479,
        train: 795
    }
}

Config.Cache = { // ⚠⚠⚠ CAUTION ⚠⚠⚠
    ttl: 30, // After x minutes unused chunks will be deleted
    cronExpression: '*/5 * * * *', // The cron expression used to run check for expired (TTL) chunks
    maxSize: 300 // Maximum amount of cached chunks. Oldest chunks will be replaced by new ones
};

// ###########################################################################################################################################
// ###########################################################################################################################################


// ####################################
// #                                  #
// #                                  #
// #     INSIDE (BASIC) GARAGES       #
// #                                  #
// #                                  #
// ####################################

Config.Interactions.Inside = {
    enabled: true,
};

Config.Garages = {
    // ##################################################################
    // #                                                                #
    // #   ALL GARAGE LOCATIONS CAN BE FOUND IN THE ./garages.js FILE   #
    // #                                                                #
    // ##################################################################
};


// ###########################################################################################################################################
// ###########################################################################################################################################


// ####################################
// #                                  #
// #                                  #
// #        HELPER FUNCTIONS          #
// #                                  #
// #                                  #
// ####################################

function vector2(x, y) {
    return { x, y };
};
```

{% endtab %}

{% tab title="garages.js" %}

```javascript
// ####################################
// #                                  #
// #                                  #
// #         GARAGE LOCATIONS         #
// #                                  #
// #                                  #
// ####################################

Config.Garages = {
    pillbox: {
        label: 'Pillbox Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [213.99, -795.06, 29.90],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // rgba
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [213.8, -809.35, 30.5],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // rgba
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [228.88, -808.9, 30.2, 162.85],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [], // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [213.8, -809.35, 30.5],
            sprite: 357,
            color: 0xc0fdfffc,
            scale: 0.7,
        },
    },
    red: {
        label: 'Red Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [-274.25, -889.94, 30.08],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-281.04, -888.05, 31.00],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-274.8002, -890.5257, 31.0806, 334.2663],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-281.04, -888.05, 31.32],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    groovestreet: {
        label: 'Groove Street Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [-51.6202, -1832.3899, 25.6743],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-52.8246, -1826.8776, 26.7011],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-51.6202, -1832.3899, 26.5743, 317.7521],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-52.8246, -1826.8776, 26.7011],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    westvinewood: {
        label: 'West Vinewood Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [71.5594, 11.8162, 67.98],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [68.4090, 13.0463, 69.2144],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [71.5594, 11.8162, 68.8892, 156.8672],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [68.03, 12.82, 69.21],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    delperro: {
        label: 'Del Perro Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [-1157.05, -745.24, 18.48],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-1159.04, -740.21, 19.39],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-1151.3291, -753.7612, 18.5272, 219.4646],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-1159.04, -740.21, 19.89],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    cypressflats: {
        label: 'Cypress Flats Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [743.73, -2017.28, 28.3],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [745.56, -2026.48, 28.79],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [743.6060, -2017.3599, 28.8795, 264.0300],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [745.56, -2026.48, 29.29],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    mirrorpark: {
        label: 'Mirror Park Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [1027.03, -758.94, 57.00],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [1036.3, -763.3, 57.50],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [1039.0846, -768.2586, 57.5937, 334.0204],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [1036.3, -763.3, 57.99],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    sandyshores2: {
        label: 'Sandy Shores 2 Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [1137.44, 2674.38, 37.28],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [1141.46, 2664.17, 37.66],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [1137.7538, 2664.3013, 37.5995, 359.8084],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [1141.46, 2664.17, 38.16],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    sandyshores: {
        label: 'Sandy Shores Garage',
        type: 'land',
        points: {
            store: {
                coords: [1711.79, 3593.21, 34.45],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [1708.7479, 3589.1423, 35.4305],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [1717.47, 3593.92, 35.29, 117.78],

        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [1708.62, 3587.43, 35.42],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    paletobay: {
        label: 'Paleto Bay Garage',
        type: 'land',
        points: {
            store: {
                coords: [81.8, 6414.7, 30.30],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [83.56, 6420.53, 31.26],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [87.9901, 6424.4248, 30.9498, 43.4845],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [83.56, 6420.53, 31.76],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    airportland: {
        label: 'Airport Garage',
        type: 'land', // land, water, air
        points: {
            store: {
                coords: [-788.01, -2032.56, 7.9],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-795.85, -2023.38, 8.68],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 36, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-793.7647, -2020.2651, 8.5240, 55.8672],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-795.85, -2023.38, 9.17],
            sprite: 357, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    airport: {
        label: 'Airport Hangar',
        type: 'air', // land, water, air
        points: {
            store: {
                coords: [-993.75, -3009.08, 12.5],
                distance: 100, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 4,
                },
            },
            menu: {
                coords: [-998.22, -3019.62, 13.95],
                distance: 50, // How far away the marker is visible
                marker: {
                    type: 33, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-993.75, -3009.08, 12.95, 55.79],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [], // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-998.22, -3019.62, 13.95],
            sprite: 359, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    sandyairport: {
        label: 'Sandy Shores Hangar',
        type: 'air', // land, water, air
        points: {
            store: {
                coords: [1698.9172, 3250.6792, 40.0],
                distance: 100, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 4,
                },
            },
            menu: {
                coords: [1726.7125, 3289.1409, 41.1686],
                distance: 50, // How far away the marker is visible
                marker: {
                    type: 33, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [1698.9172, 3250.6792, 40.9519, 104.9464],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [], // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [1740.65, 3299.57, 41.22],
            sprite: 359, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    alamosea: {
        label: 'Alamo Sea',
        type: 'water', // land, water, air
        points: {
            store: {
                coords: [1296.78, 4203.76, 30.3],
                distance: 30, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [1298.93, 4216.5, 33.91],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 35, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [1296.78, 4203.76, 30.3, 169.03],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [1298.93, 4216.5, 33.91],
            sprite: 356, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    lsymc: {
        label: 'LSYMC',
        type: 'water', // land, water, air
        points: {
            store: {
                coords: [-798.2969, -1501.9730, 0.1780],
                distance: 30, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-786.2471, -1489.6344, 1.5952],
                distance: 30, // How far away the marker is visible
                marker: {
                    type: 35, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-798.2969, -1501.9730, 0.1780, 111.2362],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-786.2471, -1489.6344, 1.5952],
            sprite: 356, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
    paletoboathouse: {
        label: 'Paleto Bay Boathouse',
        type: 'water', // land, water, air
        points: {
            store: {
                coords: [-289.2, 6637.96, 0.17],
                distance: 30, // How far away the marker is visible
                marker: {
                    type: 27, // https://docs.fivem.net/docs/game-references/markers/
                    color: [233, 167, 255, 255], // RGBA
                    rotate: true,
                    hover: false,
                    scale: 1.5,
                },
            },
            menu: {
                coords: [-277.32, 6636.93, 7.49],
                distance: 15, // How far away the marker is visible
                marker: {
                    type: 35, // https://docs.fivem.net/docs/game-references/markers/
                    color: [90, 28, 147, 255], // RGBA
                    rotate: false,
                    hover: false,
                    scale: 1.5,
                },
            },
            spawn: [-289.2, 6637.96, 0.17, 45.5],
        },
        permissions: {
            type: 'public', // public or private
            accessGroups: [] // When above option is 'private', add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this garage.
        },
        blip: {
            enabled: true,
            coords: [-277.32, 6636.93, 7.49],
            sprite: 356, // https://docs.fivem.net/docs/game-references/blips/
            color: 0xc0fdfffc, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
    },
};
```

{% endtab %}

{% tab title="impounds.js" %}

```javascript
// ####################################
// #                                  #
// #                                  #
// #       IMPOUND LOCATIONS          #
// #                                  #
// #                                  #
// ####################################

Config.Impound.locations = {
    lspdlot: {
        label: 'LSPD Lot',
        physical: true, // Whether the impounded vehicles appear as parked objects on the slots below
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
        types: { // Allowed types to be stored at the impound
            automobile: true,
            bike: true,
            boat: false,
            heli: false,
            plane: false,
            submarine: false,
            trailer: false,
            train: false,
        },
        slots: [ // physical impounds only
            [417.0617, -1627.7969, 29.2919, 136.1003],
            [419.8743, -1629.2924, 29.2919, 137.3520],
            [421.4293, -1635.8279, 29.2919, 88.0853],
            [421.0453, -1638.8785, 29.2921, 80.7112],
            [421.2091, -1641.9592, 29.2903, 87.5383],
            [418.5700, -1646.4980, 29.2920, 49.0635],
            [410.5291, -1656.8143, 29.2919, 316.4688],
            [407.8942, -1655.0428, 29.2919, 318.8490],
            [405.9343, -1652.3260, 29.2925, 317.2211],
            [403.4093, -1650.5845, 29.2941, 316.1375],
            [400.7258, -1648.6952, 29.2929, 316.7133],
            [398.2435, -1646.7185, 29.2919, 318.5959],
            [396.0146, -1644.6632, 29.2919, 316.2415],
            [410.8658, -1636.6146, 29.2919, 226.4653],
            [408.7267, -1639.0560, 29.2919, 228.3355]
        ],
        slotInfo: { // physical impounds only. Used for displaying in the menu where the vehicle is located
            slotsPerFloor: [15], // How many slots each floor has.
            floorStart: 0, // What is the lowest floor number (0 for ground floor, etc)
        },
        blip: {
            enabled: true,
            public: true, // Whether everyone can see the blip
            sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
            color: 3, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
        coords: {
            menu: [407.8860, -1625.0334, 28.2920, 224.0759],
            spawn: [402.3085, -1631.8418, 29.2919, 134.2749], // Spawn for non-physical vehicles
        },
        npcModel: 'csb_trafficwarden',
        zone: {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(408.89398193359, -1616.3669433594),
                vector2(387.22015380859, -1642.8557128906),
                vector2(410.54495239258, -1661.2303466797),
                vector2(423.38446044922, -1645.2563476562),
                vector2(424.01400756836, -1640.5024414062),
                vector2(423.89395141602, -1633.005859375),
                vector2(423.11541748047, -1628.1965332031)
            ],
            minZ: 28,
            maxZ: 33,
            debug: false,
        }
    },
    citygarage: {
        label: 'City Garage Lot',
        physical: true, // Whether the impounded vehicles appear as parked objects on the slots below
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
        types: { // Allowed types to be stored at the impound
            automobile: true,
            bike: true,
            boat: false,
            heli: false,
            plane: false,
            submarine: false,
            trailer: false,
            train: false,
        },
        slots: [ // physical impounds only
            // FLOOR 0:
            [388.0189, -1654.9271, 27.31, 140],
            [390.8403, -1657.2032, 27.31, 140],
            [393.8830, -1659.1805, 27.31, 140],
            [396.4510, -1661.0850, 27.31, 140],
            [399.3957, -1663.3911, 27.31, 140],

            [385.9437, -1670.3326, 27.31, 230],
            [383.5371, -1673.1833, 27.31, 230],
            [381.3813, -1675.7515, 27.31, 230],
            [379.0182, -1678.5687, 27.31, 230],
            [376.7709, -1681.2473, 27.31, 230],
            [374.3772, -1684.1003, 27.31, 230],
            [372.1428, -1686.7637, 27.31, 230],
            [369.8409, -1689.5066, 27.31, 230],
            [367.5732, -1692.1757, 27.31, 230],

            [364.4384, -1707.5197, 27.31, 320],
            [361.6318, -1705.2019, 27.31, 320],
            [358.8676, -1702.9187, 27.31, 320],
            [356.1306, -1700.6587, 27.31, 320],
            [353.3483, -1698.3610, 27.31, 320],
            [350.6834, -1696.1598, 27.31, 320],
            [347.8816, -1693.8462, 27.31, 320],
            [345.1746, -1691.6099, 27.31, 320],

            [354.8386, -1681.3075, 27.31, 50],
            [357.3032, -1678.5126, 27.31, 50],
            [359.5312, -1675.8445, 27.31, 50],
            [361.8633, -1673.0475, 27.31, 50],
            [364.1426, -1670.2941, 27.31, 50],
            [366.2767, -1667.7255, 27.31, 50],
            [368.5593, -1664.9727, 27.31, 50],
            [370.9476, -1662.0879, 27.31, 50],
            [373.1159, -1659.4690, 27.31, 50],

            [377.0597, -1645.7563, 27.31, 140],
            [379.7595, -1647.9932, 27.31, 140],
            [382.5834, -1650.3268, 27.31, 140],
            [385.3076, -1652.5819, 27.31, 140],

            // FLOOR 1:
            [388.0189, -1654.9271, 32.53, 140],
            [390.8403, -1657.2032, 32.53, 140],
            [393.8830, -1659.1805, 32.53, 140],
            [396.4510, -1661.0850, 32.53, 140],
            [399.3957, -1663.3911, 32.53, 140],

            [385.9437, -1670.3326, 32.53, 230],
            [383.5371, -1673.1833, 32.53, 230],
            [381.3813, -1675.7515, 32.53, 230],
            [379.0182, -1678.5687, 32.53, 230],
            [376.7709, -1681.2473, 32.53, 230],
            [374.3772, -1684.1003, 32.53, 230],
            [372.1428, -1686.7637, 32.53, 230],
            [369.8409, -1689.5066, 32.53, 230],
            [367.5732, -1692.1757, 32.53, 230],

            [364.4384, -1707.5197, 32.53, 320],
            [361.6318, -1705.2019, 32.53, 320],
            [358.8676, -1702.9187, 32.53, 320],

            [354.8386, -1681.3075, 32.53, 50],
            [357.3032, -1678.5126, 32.53, 50],
            [359.5312, -1675.8445, 32.53, 50],
            [361.8633, -1673.0475, 32.53, 50],
            [364.1426, -1670.2941, 32.53, 50],
            [366.2767, -1667.7255, 32.53, 50],
            [368.5593, -1664.9727, 32.53, 50],
            [370.9476, -1662.0879, 32.53, 50],
            [373.1159, -1659.4690, 32.53, 50],

            [377.0597, -1645.7563, 32.53, 140],
            [379.7595, -1647.9932, 32.53, 140],
            [382.5834, -1650.3268, 32.53, 140],
            [385.3076, -1652.5819, 32.53, 140],

            // FLOOR 2:
            [388.0189, -1654.9271, 37.79, 140],
            [390.8403, -1657.2032, 37.79, 140],
            [393.8830, -1659.1805, 37.79, 140],
            [396.4510, -1661.0850, 37.79, 140],
            [399.3957, -1663.3911, 37.79, 140],

            [385.9437, -1670.3326, 37.79, 230],
            [383.5371, -1673.1833, 37.79, 230],
            [381.3813, -1675.7515, 37.79, 230],
            [379.0182, -1678.5687, 37.79, 230],
            [376.7709, -1681.2473, 37.79, 230],
            [374.3772, -1684.1003, 37.79, 230],
            [372.1428, -1686.7637, 37.79, 230],
            [369.8409, -1689.5066, 37.79, 230],
            [367.5732, -1692.1757, 37.79, 230],

            [364.4384, -1707.5197, 37.79, 320],
            [361.6318, -1705.2019, 37.79, 320],
            [358.8676, -1702.9187, 37.79, 320],
            [356.1306, -1700.6587, 37.79, 320],
            [353.3483, -1698.3610, 37.79, 320],
            [350.6834, -1696.1598, 37.79, 320],
            [347.8816, -1693.8462, 37.79, 320],
            [345.1746, -1691.6099, 37.79, 320],

            [354.8386, -1681.3075, 37.79, 50],
            [357.3032, -1678.5126, 37.79, 50],
            [359.5312, -1675.8445, 37.79, 50],
            [361.8633, -1673.0475, 37.79, 50],
            [364.1426, -1670.2941, 37.79, 50],
            [366.2767, -1667.7255, 37.79, 50],
            [368.5593, -1664.9727, 37.79, 50],
            [370.9476, -1662.0879, 37.79, 50],
            [373.1159, -1659.4690, 37.79, 50],

            [377.0597, -1645.7563, 37.79, 140],
            [379.7595, -1647.9932, 37.79, 140],
            [382.5834, -1650.3268, 37.79, 140],
            [385.3076, -1652.5819, 37.79, 140],

            // FLOOR 3:
            [388.0189, -1654.9271, 43.04, 140],
            [390.8403, -1657.2032, 43.04, 140],
            [393.8830, -1659.1805, 43.04, 140],
            [396.4510, -1661.0850, 43.04, 140],
            [399.3957, -1663.3911, 43.04, 140],

            [385.9437, -1670.3326, 43.04, 230],
            [383.5371, -1673.1833, 43.04, 230],
            [381.3813, -1675.7515, 43.04, 230],
            [379.0182, -1678.5687, 43.04, 230],
            [376.7709, -1681.2473, 43.04, 230],
            [374.3772, -1684.1003, 43.04, 230],
            [372.1428, -1686.7637, 43.04, 230],
            [369.8409, -1689.5066, 43.04, 230],
            [367.5732, -1692.1757, 43.04, 230],

            [364.4384, -1707.5197, 43.04, 320],
            [361.6318, -1705.2019, 43.04, 320],
            [358.8676, -1702.9187, 43.04, 320],
            [356.1306, -1700.6587, 43.04, 320],
            [353.3483, -1698.3610, 43.04, 320],
            [350.6834, -1696.1598, 43.04, 320],
            [347.8816, -1693.8462, 43.04, 320],
            [345.1746, -1691.6099, 43.04, 320],

            [354.8386, -1681.3075, 43.04, 50],
            [357.3032, -1678.5126, 43.04, 50],
            [359.5312, -1675.8445, 43.04, 50],
            [361.8633, -1673.0475, 43.04, 50],
            [364.1426, -1670.2941, 43.04, 50],
            [366.2767, -1667.7255, 43.04, 50],
            [368.5593, -1664.9727, 43.04, 50],
            [370.9476, -1662.0879, 43.04, 50],
            [373.1159, -1659.4690, 43.04, 50],

            [377.0597, -1645.7563, 43.04, 140],
            [379.7595, -1647.9932, 43.04, 140],
            [382.5834, -1650.3268, 43.04, 140],
            [385.3076, -1652.5819, 43.04, 140],

            // FLOOR 4:
            [388.0189, -1654.9271, 48.3, 140],

            [385.9437, -1670.3326, 48.3, 230],
            [383.5371, -1673.1833, 48.3, 230],
            [381.3813, -1675.7515, 48.3, 230],
            [379.0182, -1678.5687, 48.3, 230],
            [376.7709, -1681.2473, 48.3, 230],
            [374.3772, -1684.1003, 48.3, 230],
            [372.1428, -1686.7637, 48.3, 230],
            [369.8409, -1689.5066, 48.3, 230],
            [367.5732, -1692.1757, 48.3, 230],

            [364.4384, -1707.5197, 48.3, 320],
            [361.6318, -1705.2019, 48.3, 320],
            [358.8676, -1702.9187, 48.3, 320],
            [356.1306, -1700.6587, 48.3, 320],
            [353.3483, -1698.3610, 48.3, 320],
            [350.6834, -1696.1598, 48.3, 320],
            [347.8816, -1693.8462, 48.3, 320],
            [345.1746, -1691.6099, 48.3, 320],

            [354.8386, -1681.3075, 48.3, 50],
            [357.3032, -1678.5126, 48.3, 50],
            [359.5312, -1675.8445, 48.3, 50],
            [361.8633, -1673.0475, 48.3, 50],
            [364.1426, -1670.2941, 48.3, 50],
            [366.2767, -1667.7255, 48.3, 50],
            [368.5593, -1664.9727, 48.3, 50],
            [370.9476, -1662.0879, 48.3, 50],
            [373.1159, -1659.4690, 48.3, 50],

            [377.0597, -1645.7563, 48.3, 140],
            [379.7595, -1647.9932, 48.3, 140],
            [382.5834, -1650.3268, 48.3, 140],
            [385.3076, -1652.5819, 48.3, 140],
        ],
        slotInfo: { // physical impounds only. Used for displaying in the menu where the vehicle is located
            slotsPerFloor: [35, 30, 35, 35, 31], // How many slots each floor has.
            floorStart: 0, // What is the lowest floor number (0 for ground floor, etc)
        },
        blip: {
            enabled: true,
            public: true, // Whether everyone can see the blip
            sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
            color: 3, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
        coords: {
            menu: [344.6250, -1691.8910, 31.5300, 230.6783],
            spawn: [349.8622, -1688.5133, 32.5301, 232.3330], // Spawn for non-physical vehicles
        },
        npcModel: 'csb_trafficwarden',
        zone: {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(363.58560180664, -1710.8283691406),
                vector2(385.06393432617, -1684.7681884766),
                vector2(387.70324707031, -1687.8218994141),
                vector2(394.43789672852, -1680.6389160156),
                vector2(391.78369140625, -1678.314453125),
                vector2(403.35046386719, -1663.4168701172),
                vector2(376.80191040039, -1641.1566162109),
                vector2(335.93737792969, -1689.3865966797)
            ],
            minZ: 27,
            maxZ: 49,
            debug: false,
        }
    },
    paleto: {
        label: 'Paleto Lot',
        physical: true, // Whether the impounded vehicles appear as parked objects on the slots below
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
        types: { // Allowed types to be stored at the impound
            automobile: true,
            bike: true,
            boat: false,
            heli: false,
            plane: false,
            submarine: false,
            trailer: false,
            train: false,
        },
        slots: [ // physical impounds only
            [-482.5453, 6024.6802, 31.3405, 224.9331],
            [-479.5080, 6028.5728, 31.3405, 223.5305],
            [-475.6634, 6031.4790, 31.3405, 221.7430],
            [-472.4994, 6035.4629, 31.3405, 219.5535],
            [-469.3138, 6038.9644, 31.3405, 221.5655],
            [-461.1234, 6047.4199, 31.3405, 133.8287],
            [-457.6038, 6044.2827, 31.3405, 132.2765],
            [-454.3171, 6041.0913, 31.3405, 131.8134],
            [-462.0776, 6009.5332, 31.3405, 84.0195],
            [-457.8050, 6005.5415, 31.3405, 87.3709],
            [-454.2788, 6002.0161, 31.3405, 88.3974],
            [-450.6518, 5998.4722, 31.3405, 89.3751],
            [-446.9384, 5994.6387, 31.3405, 87.8873],
        ],
        slotInfo: { // physical impounds only. Used for displaying in the menu where the vehicle is located
            slotsPerFloor: [13], // How many slots each floor has.
            floorStart: 0, // What is the lowest floor number (0 for ground floor, etc)
        },
        blip: {
            enabled: true,
            public: true, // Whether everyone can see the blip
            sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
            color: 3, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
        coords: {
            menu: [-458.0042, 6029.5151, 30.3453, 132.8405],
            spawn: [-459.4841, 6022.4023, 31.3405, 313.3645], // Spawn for non-physical vehicles
        },
        npcModel: 'csb_trafficwarden',
        zone: {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(-449.42846679688, 6024.0952148438),
                vector2(-458.18923950195, 6032.83203125),
                vector2(-449.82363891602, 6041.3061523438),
                vector2(-460.91101074219, 6052.642578125),
                vector2(-489.15676879883, 6024.4013671875),
                vector2(-478.98062133789, 6014.2026367188),
                vector2(-501.3323059082, 5991.697265625),
                vector2(-472.02935791016, 5962.3481445312),
                vector2(-450.95355224609, 5985.9189453125),
                vector2(-440.0964050293, 5994.6958007812),
                vector2(-460.97518920898, 6014.1967773438)
            ],
            minZ: 30,
            maxZ: 65,
            debug: false,
        }
    },
    paletoheli: {
        label: 'Paleto Heli Lot',
        physical: true, // Whether the impounded vehicles appear as parked objects on the slots below
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
        types: { // Allowed types to be stored at the impound
            automobile: false,
            bike: false,
            boat: false,
            heli: true,
            plane: false,
            submarine: false,
            trailer: false,
            train: false,
        },
        slots: [ // physical impounds only
            [-479.0570, 6001.3525, 31.3328, 315],
            [-476.0440, 5998.2710, 31.3355, 315],
            [-471.9939, 5994.1943, 31.3356, 315],
            [-468.4939, 5990.7676, 31.3328, 315],
            [-465.0638, 5987.3550, 31.3327, 315],
            [-461.8354, 5984.4097, 31.3134, 315],
            [-470.3217, 5976.7783, 31.3204, 135],
            [-473.5778, 5979.9819, 31.3367, 135],
            [-476.8970, 5983.1108, 31.3367, 135],
            [-480.4001, 5986.4097, 31.3367, 135],
            [-483.9145, 5989.7646, 31.3367, 135],
            [-487.3177, 5992.8926, 31.3345, 135]
        ],
        slotInfo: { // physical impounds only. Used for displaying in the menu where the vehicle is located
            slotsPerFloor: [12], // How many slots each floor has.
            floorStart: 0, // What is the lowest floor number (0 for ground floor, etc)
        },
        blip: {
            enabled: true,
            public: true, // Whether everyone can see the blip
            sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
            color: 3, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
        coords: {
            menu: [-480.1825, 5972.4185, 30.3162, 314.0261],
            spawn: [-475.7409, 5989.0352, 31.3367, 221.1059], // Spawn for non-physical vehicles
        },
        npcModel: 'csb_trafficwarden',
        zone: {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(-449.42846679688, 6024.0952148438),
                vector2(-458.18923950195, 6032.83203125),
                vector2(-449.82363891602, 6041.3061523438),
                vector2(-460.91101074219, 6052.642578125),
                vector2(-489.15676879883, 6024.4013671875),
                vector2(-478.98062133789, 6014.2026367188),
                vector2(-501.3323059082, 5991.697265625),
                vector2(-472.02935791016, 5962.3481445312),
                vector2(-450.95355224609, 5985.9189453125),
                vector2(-440.0964050293, 5994.6958007812),
                vector2(-460.97518920898, 6014.1967773438)
            ],
            minZ: 30,
            maxZ: 100,
            debug: false,
        }
    },
    plane: {
        label: 'Fort Zancudo Plane Lot',
        physical: true, // Whether the impounded vehicles appear as parked objects on the slots below
        accessGroups: ['police'], // Add groups (jobs, gangs), CIDs or group-grade pairs (jobs: { police: 0 }) that can access this impound.
        types: { // Allowed types to be stored at the impound
            automobile: false,
            bike: false,
            boat: false,
            heli: false,
            plane: true,
            submarine: false,
            trailer: false,
            train: false,
        },
        slots: [ // physical impounds only
            [-2233.5740, 3270.9724, 32.8102, 235.5],
            [-2247.6902, 3246.6460, 32.8102, 235.5],
            [-2261.1846, 3222.1101, 32.8102, 235.5],
            [-2285.4028, 3180.8767, 32.8100, 235.5],
            [-2184.5352, 3176.1553, 32.8101, 328.5],
            [-2117.3496, 3142.7307, 32.8101, 328.5],
            [-2051.2705, 3106.4106, 32.8103, 328.5],
            [-1985.7488, 3069.9590, 32.8103, 328.5],
            [-1921.1477, 3031.8638, 32.8103, 328.5]
        ],
        slotInfo: { // physical impounds only. Used for displaying in the menu where the vehicle is located
            slotsPerFloor: [9], // How many slots each floor has.
            floorStart: 0, // What is the lowest floor number (0 for ground floor, etc)
        },
        blip: {
            enabled: true,
            public: true, // Whether everyone can see the blip
            sprite: 317, // https://docs.fivem.net/docs/game-references/blips/
            color: 3, // https://docs.fivem.net/docs/game-references/blips/#blip-colors
            scale: 0.7,
        },
        coords: {
            menu: [-2177.8450, 3244.3687, 31.8103, 153.9037],
            spawn: [-2243.4131, 3189.3015, 32.8083, 150.0], // Spawn for non-physical vehicles
        },
        npcModel: 'csb_trafficwarden',
        zone: {
            points: [ // PolyZone points. You can use '/pzcreate poly', '/pzadd' and '/pzfinish' for easier creation.
                vector2(-2838.3647460938, 3391.1774902344),
                vector2(-2883.25390625, 3313.169921875),
                vector2(-2802.3115234375, 3260.9848632812),
                vector2(-2749.2978515625, 3191.0715332031),
                vector2(-2531.9575195312, 3062.5119628906),
                vector2(-2503.0441894531, 2930.8203125),
                vector2(-2446.8991699219, 2896.9765625),
                vector2(-2408.8156738281, 2928.7573242188),
                vector2(-2349.3151855469, 2943.2277832031),
                vector2(-2270.5043945312, 2919.7121582031),
                vector2(-2154.6674804688, 2845.8403320312),
                vector2(-2102.9719238281, 2792.2321777344),
                vector2(-2020.6541748047, 2813.0895996094),
                vector2(-1837.5306396484, 2762.6809082031),
                vector2(-1804.7650146484, 2810.3110351562),
                vector2(-1717.3735351562, 2815.8823242188),
                vector2(-1683.6169433594, 2872.9946289062),
                vector2(-1662.8596191406, 2872.87890625),
                vector2(-1614.3549804688, 2807.1120605469),
                vector2(-1598.4055175781, 2822.4384765625),
                vector2(-1650.7803955078, 2900.6938476562),
                vector2(-1682.2434082031, 2995.951171875),
                vector2(-1703.7517089844, 3137.5415039062),
                vector2(-1666.8693847656, 3228.0993652344),
                vector2(-1965.7473144531, 3396.05859375),
                vector2(-2047.0560302734, 3331.2614746094),
                vector2(-2127.2712402344, 3377.1843261719)
            ],
            minZ: 30,
            maxZ: 250,
            debug: true,
        }
    },
}
```

{% endtab %}

{% tab title="lang.js" %}

```javascript
const Lang = {};

Lang.Errors = {
    vehicle_not_found: 'Vehicle not found!',
    vehicle_hash_missing: 'Vehicle not registered! Contact an admin!',
    no_park_inside: 'You can\'t park the vehicle while you\'re inside. Please get out!',
    no_park_passengers: 'You can\'t park the vehicle while there are passengers inside!',
    no_park_inuse: 'You can\'t park the vehicle while it is in use!',
    no_park: 'You can\'t park this vehicle!',
    no_park_here: 'You can\'t park vehicles here!',
    no_park_ground: 'Vehicle must be on the ground upright to park!',
    ped_not_found: 'Ped not found!',
    out_of_bounds: 'Out of bound!',
    invalid_fields: 'Invalid fields!',
    park_failed: 'Failed to park vehicle',
    cid_missing: 'Could not get player\'s Citizen ID/Identifier',
    vehicle_distance: 'Vehicle is too far or not found!',
    vehicle_blacklisted: 'Vehicle is blacklisted and cannot be parked here!',
    vehicle_owner_missing: 'Nobody owns this vehicle and it can\'t be parked!',
    vehicle_not_owned: 'You don\'t own this vehicle!',
    already_parked: 'Vehicle is already parked somewhere!',
    busy: 'You can\'t do this right now!',
    no_permission: 'You don\'t have permissions to do this!',
    invalid_garage: 'Invalid garage!',
    garage_distance: 'Garage not found or too far away!',
    space_occupied: 'There is something in the way!',
    vehicle_owner_cid_missing: 'Vehicle owner Citizen ID could not be found!',
    invalid_depot: 'Invalid depot!',
    invalid_impound: 'Invalid impound!',
    depot_distance: 'Depot not found or too far away!',
    impound_distance: 'Impound not found or too far away!',
    vehicle_type_mismatch: 'This vehicle type can not be stored inside this garage!',
    depot_type_mismatch: 'This vehicle can not be taken out of this depot type!',
    no_money: 'You don\'t have enough money!',
    impound_type_denied: 'This impound can not store vehicles of this type!',
    impound_slots_error: 'An error occured while managing the impound\'s free slots. Please contact an admin.',
    impound_full: 'The impound is full! Please choose another one.',
    impound_slot_invalid: 'Invalid impound slot!',
    impound_slot_occupied: 'The slot you\'ve chosen is already occupied!',
    not_allowed: 'This action is not allowed!',
    keys_player_not_found: 'The player you are trying to give keys to can not be found!',
};

Lang.Messages = {
    vehicle_parked: 'Vehicle parked!',
    vehicle_spawned: 'Vehicle spawned!',
    garage_empty: 'Garage is empty.',
    depot_empty: 'You don\'t have any depot vehicles of this type.',
    vehicle_jacked: 'Succesfully jacked this vehicle!',
    vehicle_not_impounded: 'Vehicle was removed but has not been impounded, because it was not player owned.',
    vehicle_impounded: 'Vehicle impounded!',
    impound_purged: 'Impound succesfully purged - vehicles %s',
    vehicle_transferred: 'Vehicle transferred!',
    vehicle_released: 'Vehicle released from impound!',
    vehicle_spawned_impound: 'Vehicle spawned at impound!',
    vehicle_marked: 'Vehicle marked on map!',
    remote_keys_received: 'You got the keys to a nearby remotely unparked vehicle!',
    vehicle_removed: 'Vehicle removed!',
};

Lang.TextUIs = {
    store_vehicle: '[E] - Store Vehicle',
    open_garage: '[E] - Open Garage',
    open_depot: '[E] - Open Depot',
    open_impound: '[E] - Open Impound',
};

Lang.Misc = {
    public_blip_name: 'Public Parking',
    parked_blip_name: 'Parked Vehicle',
    depot_blip_name: 'Depot',
    depot_charge_reason: 'Took vehicle out of depot',
    impound_blip_name: 'Impound',
};

Lang.Menus = {
    price_format: '$%s',
    percent_format: '%s%',
    confirm_depot: {
        title: 'Confirm Depot Payment',
        description: 'Take out **%s** for **$%s**?',
    },
    minigame_start: {
        title: 'Start Alarm Disable Sequence?',
        description: 'Would you like to start the hack sequence to disable the alarm?'
    },
    minigame_failed: {
        title: 'Alarm Disabling Failed',
        description: 'Would you like to try again? \n If you cancel, re-enter the vehicle to try again.'
    },
    depot_title: '%s Depot: %s',
    impound: {
        title: 'Impound Lot Options',
        search_vehicle: {
            title: 'Find Vehicle',
            description: 'Search for a vehicle in all impound locations'
        },
        list_vehicles: {
            title: 'All Vehicles',
            description: 'List of all vehicles in this current impound'
        },
        purge_impound: 'Purge Impound',
        purge_dialog: {
            title: 'Confirm Impound Purge',
            description: 'Are you sure you want to **%s** **ALL** vehicles from this lot? \n This action is irreversible!',
            despawn: 'despawn',
            depot: 'depot',
            delete: 'permanently delete'
        },
        search_dialog: {
            title: 'Search Vehicles',
            input: {
                title: 'Plate, Name or CID',
                description: 'Enter vehicle plate, owner name or CID',
            }
        },
        search_results: 'Search Results (%s)',
        all_vehicles: 'Vehicles In Current Lot (%s)',
        vehicle_footer: 'Owner: %s | Impounded: %s',
        vehicle_details: {
            title: 'Impound Vehicle Details',
            make: 'Make',
            model: 'Model',
            plate: 'License Plate',
            owner_cid: 'Owner',
            impounded_at: 'Impounded At',
            impounded_by: 'Impounded By',
            impound: 'Impound where vehicle is located',
            impound_slot: {
                title: 'Floor %s, Slot %s',
                description: 'Vehicle location inside impound'
            },
            transfer_vehicle: 'Transfer Vehicle',
            release_vehicle: 'Release Vehicle',
            spawn_vehicle: 'Spawn Vehicle',
            highlight_vehicle: 'Highlight Vehicle',
        },
        select_impound_dialog: {
            title: 'Impound Vehicle',
            input: 'Select Impound Lot'
        },
        give_keys_dialog: {
            title: 'Give Vehicle Keys',
            input: 'Select Player',
        },
        transfer_dialog: {
            title: 'Transfer Vehicle',
            description: 'Are you sure you want to transfer this vehicle to **%s** for **%s**?'
        }
    },
    garage: {
        title: 'My Garage',
        search_vehicles: {
            title: 'Search Vehicles',
            description: 'Search all your owned vehicles'
        },
        all_vehicles: {
            title: 'All Your Vehicles',
            description: 'List of all vehicles you own'
        },
        outside_vehicles: {
            title: 'Outside Vehicles',
            description: 'Vehicles parked outside on the street',
        },
        inside_vehicles: {
            title: 'Inside Vehicles',
            description: 'Vehicles parked inside a garage'
        },
        spawned_vehicles: {
            title: 'Spawned Vehicles',
            description: 'Vehicles that are spawned on the map',
        },
        impounded_vehicles: {
            title: 'Impounded Vehicles',
            description: 'Vehicles that have been confiscated by the police'
        },
        search_dialog: {
            title: 'Search Vehicles',
            input: {
                title: 'Plate, Model, Garage or Type',
                description: 'Types = automobile, bike, boat, heli, plane',
            }
        },
        search_results: 'Search Results (%s)',
        vehicle_footer: 'Status: %s',
        all_vehicles_header: 'All Vehicles (%s)',
        outside_vehicles_header: 'Outside Vehicles (%s)',
        outside_vehicle_footer: 'Parked At: %s',
        inside_vehicles_header: 'Inside Vehicles (%s)',
        inside_vehicles_footer: 'Garage: %s',
        spawned_vehicles_header: 'Spawned Vehicles (%s)',
        impound_vehicles_header: 'Impounded Vehicles (%s)',
        impound_vehicles_footer: 'Impound Name: %s',
        vehicle_details: {
            title: 'Vehicle Details',
            make: 'Make',
            model: 'Model',
            plate: 'License Plate',
            status: 'Status',
            parked_at: 'Parked At',
            impound: 'Impound where vehicle is located',
            impounded_at: 'Impounded At',
            impounded_by: 'Impounded By',
            garage: 'Garage',
            body: 'Body Health',
            engine: 'Engine Health',
            fuel: 'Fuel Level',
            unpark_vehicle: 'Unpark Vehicle',
            mark_vehicle: 'Mark Vehicle',
        },
        give_keys_dialog: {
            title: 'Give Vehicle Keys',
            description: 'These are the closest people to your vehicle',
            input: 'Select Player',
            no_players: 'No nearby players',
        },
    },
    no_results: 'No results',
};
```

{% endtab %}
{% endtabs %}


